6.12.109-rc1 review
Greg Kroah-Hartman
maintainerPATCH 282/403 power: supply: max17040: propagate register read errors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jianing Li <[email protected]>
commit 659cc3d8d5ef246263873fce72c8cadeeed073cc upstream.
max17040_get_vcell() and max17040_get_soc() ignore errors returned by regmap_read(). When an I2C transfer fails, the uninitialized register value is converted and reported to userspace as a valid voltage or state of charge. The polling worker can also replace the cached state of charge with the bogus value and emit a spurious change event.
Propagate read errors through the power supply get_property callback and keep the last valid cached state of charge when polling fails.
--- drivers/power/supply/max17040_battery.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-)
drivers/power/supply/max17040_battery.c +21 -5
@@ -192,8 +192,11 @@ static int max17040_raw_vcell_to_uvolts(static int max17040_get_vcell (struct max17040_chip *chip) {u32 vcell; +int ret; -regmap_read (chip->regmap,MAX17040_VCELL , &vcell); + ret =regmap_read (chip->regmap,MAX17040_VCELL , &vcell); +if (ret) +return ret;return max17040_raw_vcell_to_uvolts (chip, vcell); }
@@ -201,8 +204,11 @@ static int max17040_get_vcell(struct maxstatic int max17040_get_soc (struct max17040_chip *chip) {u32 soc; +int ret; -regmap_read (chip->regmap,MAX17040_SOC , &soc); + ret =regmap_read (chip->regmap,MAX17040_SOC , &soc); +if (ret) +return ret;return soc >> (chip->quirk_double_soc ?9 :8 ); }
@@ -261,7 +267,11 @@ static int max17040_get_of_data(struct mstatic void max17040_check_changes (struct max17040_chip *chip) { - chip->soc =max17040_get_soc (chip); +int soc; + + soc =max17040_get_soc (chip); +if (soc >=0 ) + chip->soc = soc; }static void max17040_queue_work (struct max17040_chip *chip)
@@ -396,10 +406,16 @@ static int max17040_get_property(struct val->intval =max17040_get_online (chip);break ;case POWER_SUPPLY_PROP_VOLTAGE_NOW : - val->intval =max17040_get_vcell (chip); + ret =max17040_get_vcell (chip); +if (ret <0 ) +return ret; + val->intval = ret;break ;case POWER_SUPPLY_PROP_CAPACITY : - val->intval =max17040_get_soc (chip); + ret =max17040_get_soc (chip); +if (ret <0 ) +return ret; + val->intval = ret;break ;case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN : val->intval = chip->low_soc_alert;
Greg Kroah-Hartman
maintainerPATCH 283/403 power: supply: max17040: drop incorrect I2C functionality check
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jianing Li <[email protected]>
commit 4e4b9f5ce9dfb8ed4b8d1262a504b8043ac09d87 upstream.
max17040_probe() rejects adapters that do not advertise I2C_FUNC_SMBUS_BYTE. The driver does not issue SMBus byte transactions, however. Its regmap has 8-bit registers and 16-bit big-endian values, for which regmap-i2c supports either raw I2C transfers or SMBus word-data transactions.
Consequently, an adapter providing raw I2C transfers or SMBus word data but not SMBus byte transactions is rejected even though regmap can access the device. Conversely, the current check can pass an adapter that regmap cannot use.
Drop the stale check and let devm_regmap_init_i2c() validate and select the supported transfer method.
--- drivers/power/supply/max17040_battery.c | 4 ---- 1 file changed, 4 deletions(-)
drivers/power/supply/max17040_battery.c +0 -4
@@ -474,16 +474,12 @@ static const struct power_supply_desc mastatic int max17040_probe (struct i2c_client *client) {const struct i2c_device_id *id =i2c_client_get_device_id (client); -struct i2c_adapter *adapter = client->adapter;struct power_supply_config psy_cfg = {};struct max17040_chip *chip;enum chip_id chip_id;bool enable_irq = false;int ret; -if (!i2c_check_functionality (adapter,I2C_FUNC_SMBUS_BYTE )) -return -EIO ; - chip =devm_kzalloc (&client->dev,sizeof (*chip),GFP_KERNEL );if (!chip)return -ENOMEM ;
Greg Kroah-Hartman
maintainerPATCH 284/403 power: supply: max17040: synchronize work cancellation on suspend
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jianing Li <[email protected]>
commit 86a3a8a926aa5969c329d1df2d3259f189961bbc upstream.
max17040_work() requeues itself after every poll. cancel_delayed_work() only cancels a pending instance and does not wait for a callback that is already running.
If system suspend races with the polling callback, the callback can continue accessing the fuel gauge and requeue itself after the suspend callback returns.
Use cancel_delayed_work_sync() to ensure polling is quiesced before suspend completes.
--- drivers/power/supply/max17040_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/max17040_battery.c +1 -1
@@ -580,7 +580,7 @@ static int max17040_suspend(struct devic// disable soc alert to prevent wakeup max17040_set_soc_alert (chip,0 ); else - cancel_delayed_work(&chip->work); + cancel_delayed_work_sync(&chip->work);if (client->irq &&device_may_wakeup (dev))enable_irq_wake (client->irq);
Greg Kroah-Hartman
maintainerPATCH 285/403 s390/cpum_cf: Handle CPU hotplug via prepare/dead callbacks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Richter <[email protected]>
commit 337bd95507a16063687cfc286ea90de5cca48c37 upstream.
The command 'perf stat -e cycles -- <command>' crashes the kernel when CPUs are hotplug added during that run.
Root cause is the allocation of struct cpu_cf_events at first event initialization. The allocation is dynamic and the first event that has task context creates such a structure for each online CPU. This is not sufficient. CPUs may be offline during event creation and can be set online during the perf run time. For example commands
# echo 0 > /sys/devices/system/cpu/cpu1/online # perf stat -e cycles -i -- stress-ng -t10s --matrix X # sleep 1 # echo 1 > /sys/devices/system/cpu/cpu1/online
create an event for CPUs 0,2-X. Since the events are created with task-context, the scheduler will eventually schedule the program on CPU1. This CPU has not created and initialized any per CPU event infrastructure as that CPU was not online at the time of the perf invocation. Thus when the scheduler runs stress-ng on CPU1, the function cpumf_pmu_add() refers to a NULL pointer:
struct cpu_cf_events *cpuhw = this_cpu_cfhw();
This function call is invoked after the task stress-ng has been made runnable on CPU1. And this_cpu_cfhw() returns NULL.
The result is a panic:
Unable to handle kernel pointer dereference in virtual kernel address space
Failing address: 0000000000000000 TEID: 0000000000000483
....
Krnl PSW : 0404d00180000000 000003ef8291fd0c (cpumf_pmu_add+0x3c/0x80)
....
Call Trace: [<000003ef8291fd0c>] cpumf_pmu_add+0x3c/0x80 [<000003ef82bb5e3e>] event_sched_in+0xae/0x190 [<000003ef82bb60d6>] merge_sched_in+0x1b6/0x390 [<000003ef82bb65b8>] visit_groups_merge.constprop.0.isra.0+0x308/0x5b0 [<000003ef82bb689a>] pmu_groups_sched_in+0x3a/0x50 [<000003ef82bb6a30>] ctx_sched_in+0x180/0x260 [<000003ef82bb780c>] perf_event_context_sched_in+0x11c/0x2d0 [<000003ef82bb79ee>] __perf_event_task_sched_in+0x2e/0xc0 [<000003ef82994834>] finish_task_switch.isra.0+0x1a4/0x250
....
Last Breaking-Event-Address:
[<000003ef8291f1d8>] this_cpu_cfhw+0x38/0x40
The issue arises only in per-task context when the CPUMF facility is used and the scheduler picks a random CPU for such a process to run on. The scheduler enables the CPUMF infrastructure via PMU callback functions pmu::add() and pmu::del().
Introduce a CPU hotplug prepare/dead callback pair which creates and removes the per CPU counter data while the CPU is offline. Count the users which track every CPU (cpu == -1), that is perf_event_open() events with task context and /dev/hwctr device sessions, in the new counter cpu_cf_root::tskcnt, protected by pmc_reserve_mutex. This ensures the infrastructure is available when
new CPU is selected to run the per-task context process.
In cpum_cf_free_root() and cpum_cf_free_cpu() ensure the reference pointer to data structures is set to NULL before the data is freed to prevent interrupt handlers to access stale data.
--- arch/s390/kernel/perf_cpum_cf.c | 215 ++++++++++++++++++++++++++-------------- 1 file changed, 142 insertions(+), 73 deletions(-)
arch/s390/kernel/perf_cpum_cf.c +142 -73
@@ -112,6 +112,7 @@ struct cpu_cf_ptr {static struct cpu_cf_root {/* Anchor to per CPU data */ refcount_t refcnt;/* Overall active events */ +unsigned int tskctx;/* Users tracking all CPUs (cpu == -1) */ struct cpu_cf_ptr __percpu *cfptr; } cpu_cf_root;
@@ -120,13 +121,15 @@ static struct cpu_cf_root { /* Anchor t* user space in task context with perf_event_open() and close() * system calls. * -* This mutex serializes functions cpum_cf_alloc_cpu() called at event -* initialization via cpumf_pmu_event_init() and function cpum_cf_free_cpu() -* called at event removal via call back function hw_perf_event_destroy() -* when the event is deleted. They are serialized to enforce correct -* bookkeeping of pointer and reference counts anchored by -* struct cpu_cf_root and the access to cpu_cf_root::refcnt and the -* per CPU pointers stored in cpu_cf_root::cfptr. +* This mutex serializes the allocation and removal of the per CPU counter +* data via cpum_cf_alloc_cpu() and cpum_cf_free_cpu(). They are called with +* this mutex held at event initialization via cpumf_pmu_event_init(), at +* event removal via call back function hw_perf_event_destroy() when the +* event is deleted, and from the CPU hotplug prepare/dead callbacks. The +* mutex enforces correct bookkeeping of pointer and reference counts +* anchored by struct cpu_cf_root and protects the access to +* cpu_cf_root::refcnt, cpu_cf_root::tskctx and the per CPU pointers +* stored in cpu_cf_root::cfptr. */ static DEFINE_MUTEX (pmc_reserve_mutex);
@@ -169,12 +172,14 @@ static void cpum_cf_reset_cpu(void *flag }/* Free per CPU data when the last event is removed. */ -static void cpum_cf_free_root (void ) +static void cpum_cf_free_root (unsigned int num) { -if (!refcount_dec_and_test (&cpu_cf_root.refcnt)) +struct cpu_cf_ptr __percpu *p = cpu_cf_root.cfptr; + +if (!refcount_sub_and_test (num, &cpu_cf_root.refcnt))return ; -free_percpu (cpu_cf_root.cfptr); cpu_cf_root.cfptr =NULL ; +free_percpu (p);irq_subclass_unregister (IRQ_SUBCLASS_MEASUREMENT_ALERT );on_each_cpu (cpum_cf_reset_cpu,NULL ,1 ); debug_sprintf_event(cf_dbg, 4, "%s root.refcnt %u cfptr %d\n",
@@ -188,17 +193,17 @@ static void cpum_cf_free_root(void)* CPUs possible, which might be larger than the number of CPUs currently * online. */ -static int cpum_cf_alloc_root (void ) +static int cpum_cf_alloc_root (unsigned int num) {int rc =0 ; -if (refcount_inc_not_zero (&cpu_cf_root.refcnt)) +if (refcount_add_not_zero (num, &cpu_cf_root.refcnt))return rc;/* The memory is already zeroed. */ cpu_cf_root.cfptr = alloc_percpu(struct cpu_cf_ptr);if (cpu_cf_root.cfptr) { -refcount_set (&cpu_cf_root.refcnt,1 ); +refcount_set (&cpu_cf_root.refcnt, num);on_each_cpu (cpum_cf_reset_cpu,NULL ,1 );irq_subclass_register (IRQ_SUBCLASS_MEASUREMENT_ALERT ); }else {
@@ -208,20 +213,23 @@ static int cpum_cf_alloc_root(void)return rc; } -/* Free CPU counter data structure for a PMU */ -static void cpum_cf_free_cpu (int cpu) +/* +* Remove num references to the CPU counter data structure of a PMU. +* Called with pmc_reserve_mutex held. +*/ +static void cpum_cf_free_cpu (int cpu,unsigned int num) {struct cpu_cf_events *cpuhw;struct cpu_cf_ptr *p; -mutex_lock (&pmc_reserve_mutex); +lockdep_assert_held (&pmc_reserve_mutex);/* * When invoked via CPU hotplug handler, there might be no events * installed or that particular CPU might not have an * event installed. This anchor pointer can be NULL! */ if (!cpu_cf_root.cfptr) - goto out; +return ; p =per_cpu_ptr (cpu_cf_root.cfptr, cpu); cpuhw = p->cpucf;/*
@@ -229,28 +237,29 @@ static void cpum_cf_free_cpu(int cpu)* installed on that CPU, but on different CPUs. */ if (!cpuhw) - goto out; +return ; -if (refcount_dec_and_test (&cpuhw->refcnt)) { -kfree (cpuhw); +if (refcount_sub_and_test (num, &cpuhw->refcnt)) { p->cpucf =NULL ; +kfree (cpuhw); } -cpum_cf_free_root (); -out: -mutex_unlock (&pmc_reserve_mutex); +cpum_cf_free_root (num); } -/* Allocate CPU counter data structure for a PMU. Called under mutex lock. */ -static int cpum_cf_alloc_cpu (int cpu) +/* +* Add num references to the CPU counter data structure of a PMU and +* allocate it when necessary. Called with pmc_reserve_mutex held. +*/ +static int cpum_cf_alloc_cpu (int cpu,unsigned int num) {struct cpu_cf_events *cpuhw;struct cpu_cf_ptr *p;int rc; -mutex_lock (&pmc_reserve_mutex); - rc =cpum_cf_alloc_root (); +lockdep_assert_held (&pmc_reserve_mutex); + rc =cpum_cf_alloc_root (num);if (rc) - goto unlock; +return rc; p =per_cpu_ptr (cpu_cf_root.cfptr, cpu); cpuhw = p->cpucf;
@@ -258,12 +267,12 @@ static int cpum_cf_alloc_cpu(int cpu) cpuhw =kzalloc (sizeof (*cpuhw),GFP_KERNEL );if (cpuhw) { p->cpucf = cpuhw; -refcount_set (&cpuhw->refcnt,1 ); +refcount_set (&cpuhw->refcnt, num); }else { rc = -ENOMEM ; } }else { -refcount_inc (&cpuhw->refcnt); +refcount_add (num, &cpuhw->refcnt); }if (rc) {/*
@@ -271,10 +280,8 @@ static int cpum_cf_alloc_cpu(int cpu)* cpu_cf_event in not created, its destroy() function is not * invoked. Adjust the reference counter for the anchor. */ -cpum_cf_free_root (); +cpum_cf_free_root (num); } -unlock: -mutex_unlock (&pmc_reserve_mutex);return rc; }
@@ -286,39 +293,70 @@ unlock:* perf_event_open() with task context and /dev/hwctr interface. * If cpu is non-zero install event on this CPU only. This setup handles * perf_event_open() with CPU context. +* Users with cpu == -1 are counted in cpu_cf_root::tskctx. The CPU hotplug +* prepare and dead callbacks use this count to install and remove the per +* CPU counter data on a new or dying CPU. */ -static int cpum_cf_alloc (int cpu) +static int cpum_cf_alloc_cpuslocked (int cpu) {cpumask_var_t mask;int rc; +lockdep_assert_cpus_held ();if (cpu ==-1 ) {if (!zalloc_cpumask_var (&mask,GFP_KERNEL ))return -ENOMEM ; +mutex_lock (&pmc_reserve_mutex);for_each_online_cpu (cpu) { - rc =cpum_cf_alloc_cpu (cpu); + rc =cpum_cf_alloc_cpu (cpu,1 );if (rc) {for_each_cpu (cpu, mask) -cpum_cf_free_cpu (cpu); +cpum_cf_free_cpu (cpu,1 );break ; }cpumask_set_cpu (cpu, mask); } +if (!rc) + cpu_cf_root.tskctx++; +mutex_unlock (&pmc_reserve_mutex);free_cpumask_var (mask); }else { - rc =cpum_cf_alloc_cpu (cpu); +mutex_lock (&pmc_reserve_mutex); + rc =cpum_cf_alloc_cpu (cpu,1 ); +mutex_unlock (&pmc_reserve_mutex); }return rc; } -static void cpum_cf_free (int cpu) +static int cpum_cf_alloc (int cpu) +{ +int rc; + +cpus_read_lock (); + rc =cpum_cf_alloc_cpuslocked (cpu); +cpus_read_unlock (); +return rc; +} + +static void cpum_cf_free_cpuslocked (int cpu) { +lockdep_assert_cpus_held (); +mutex_lock (&pmc_reserve_mutex);if (cpu ==-1 ) { + cpu_cf_root.tskctx--;for_each_online_cpu (cpu) -cpum_cf_free_cpu (cpu); +cpum_cf_free_cpu (cpu,1 ); }else { -cpum_cf_free_cpu (cpu); +cpum_cf_free_cpu (cpu,1 ); } +mutex_unlock (&pmc_reserve_mutex); +} + +static void cpum_cf_free (int cpu) +{ +cpus_read_lock (); +cpum_cf_free_cpuslocked (cpu); +cpus_read_unlock (); }#define CF_DIAG_CTRSET_DEF 0xfeef/* Counter set header mark */
@@ -1094,53 +1132,67 @@ static refcount_t cfset_opencnt = REFCOUstatic DEFINE_MUTEX (cfset_ctrset_mutex);/* -* CPU hotplug handles only /dev/hwctr device. -* For perf_event_open() the CPU hotplug handling is done on kernel common -* code: +* CPU hotplug handling: +* +* cpum_cf_prepare_cpu() and cpum_cf_dead_cpu() run while the new or dying +* CPU is offline. They create and remove the per CPU counter data for all +* users tracking every CPU (cpu == -1), that is perf_event_open() events +* with task context and /dev/hwctr device sessions. Each such user holds +* one reference to the per CPU counter data of each CPU. Therefore install +* and remove one reference per user, tracked in cpu_cf_root::tskctx. This +* guarantees the per CPU counter data exists before the new CPU executes +* its first task and is removed only after the dying CPU is gone. +* +* cpum_cf_online_cpu() and cpum_cf_offline_cpu() run while the new or +* dying CPU is online. They handle only the counter set state of open +* /dev/hwctr device sessions on that CPU. For perf_event_open() events +* nothing is done: * - CPU add: Nothing is done since a file descriptor can not be created * and returned to the user. * - CPU delete: Handled by common code via pmu_disable(), pmu_stop() and -* pmu_delete(). The event itself is removed when the file descriptor is -* closed. +* pmu_delete(). During task exit processing of grouped perf events +* triggered by CPU hotplug processing, pmu_disable() is called as part +* of perf context removal process. The event itself is removed when the +* event file descriptor is closed. */ +static int cpum_cf_prepare_cpu (unsigned int cpu) +{ +int rc =0 ; + +mutex_lock (&pmc_reserve_mutex); +if (cpu_cf_root.tskctx) + rc =cpum_cf_alloc_cpu (cpu, cpu_cf_root.tskctx); +mutex_unlock (&pmc_reserve_mutex); +return rc; +} + +static int cpum_cf_dead_cpu (unsigned int cpu) +{ +mutex_lock (&pmc_reserve_mutex); +if (cpu_cf_root.tskctx) +cpum_cf_free_cpu (cpu, cpu_cf_root.tskctx); +mutex_unlock (&pmc_reserve_mutex); +return 0 ; +} +static int cfset_online_cpu (unsigned int cpu);static int cpum_cf_online_cpu (unsigned int cpu) { -int rc =0 ; - -/* -* Ignore notification for perf_event_open(). -* Handle only /dev/hwctr device sessions. -*/ mutex_lock (&cfset_ctrset_mutex); -if (refcount_read (&cfset_opencnt)) { - rc =cpum_cf_alloc_cpu (cpu); -if (!rc) -cfset_online_cpu (cpu); - } +if (refcount_read (&cfset_opencnt)) +cfset_online_cpu (cpu);mutex_unlock (&cfset_ctrset_mutex); -return rc; +return 0 ; }static int cfset_offline_cpu (unsigned int cpu);static int cpum_cf_offline_cpu (unsigned int cpu) { -/* -* During task exit processing of grouped perf events triggered by CPU -* hotplug processing, pmu_disable() is called as part of perf context -* removal process. Therefore do not trigger event removal now for -* perf_event_open() created events. Perf common code triggers event -* destruction when the event file descriptor is closed. -* -* Handle only /dev/hwctr device sessions. -*/ mutex_lock (&cfset_ctrset_mutex); -if (refcount_read (&cfset_opencnt)) { +if (refcount_read (&cfset_opencnt))cfset_offline_cpu (cpu); -cpum_cf_free_cpu (cpu); - }mutex_unlock (&cfset_ctrset_mutex);return 0 ; }
@@ -1187,7 +1239,7 @@ static void cpumf_measurement_alert(strustatic int cfset_init (void );static int __init cpumf_pmu_init (void ) { -int rc; +int state, rc;/* Extract counter measurement facility information */ if (!cpum_cf_avail () ||qctri (&cpumf_ctr_info))
@@ -1229,11 +1281,24 @@ static int __init cpumf_pmu_init(void)cfset_init (); } + rc =cpuhp_setup_state (CPUHP_BP_PREPARE_DYN , +"perf/s390/cf:prepare" , + cpum_cf_prepare_cpu, cpum_cf_dead_cpu); +if (rc <0 ) + goto out3; + state = rc; + rc =cpuhp_setup_state (CPUHP_AP_PERF_S390_CF_ONLINE ,"perf/s390/cf:online" , cpum_cf_online_cpu, cpum_cf_offline_cpu); -return rc; +if (rc <0 ) + goto out4; +return 0 ; +out4: +cpuhp_remove_state (state); +out3: +perf_pmu_unregister (&cpumf_pmu); out2:debug_unregister_view (cf_dbg, &debug_sprintf_view);debug_unregister (cf_dbg);
@@ -1389,6 +1454,7 @@ static void cfset_all_stop(struct cfset_*/ static int cfset_release (struct inode *inode,struct file *file) { +cpus_read_lock ();mutex_lock (&cfset_ctrset_mutex);/* Open followed by close/exit has no private_data */ if (file->private_data) {
@@ -1399,9 +1465,10 @@ static int cfset_release(struct inode *i }if (refcount_dec_and_test (&cfset_opencnt)) {/* Last close */ on_each_cpu (cfset_release_cpu,NULL ,1 ); -cpum_cf_free (-1 ); +cpum_cf_free_cpuslocked (-1 ); }mutex_unlock (&cfset_ctrset_mutex); +cpus_read_unlock ();return 0 ; }
@@ -1420,15 +1487,17 @@ static int cfset_open(struct inode *inodreturn -EPERM ; file->private_data =NULL ; +cpus_read_lock ();mutex_lock (&cfset_ctrset_mutex);if (!refcount_inc_not_zero (&cfset_opencnt)) {/* First open */ - rc =cpum_cf_alloc (-1 ); + rc =cpum_cf_alloc_cpuslocked (-1 );if (!rc) {cfset_session_init ();refcount_set (&cfset_opencnt,1 ); } }mutex_unlock (&cfset_ctrset_mutex); +cpus_read_unlock ();/* nonseekable_open() never fails */ return rc ?:nonseekable_open (inode, file);
Greg Kroah-Hartman
maintainerPATCH 286/403 s390/dasd: Do not complete a failed ESE read as successful
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Haberland <[email protected]>
commit cddb447c62466f3076938ce120028d7b591f9f37 upstream.
dasd_int_handler() completes an NRF read of an unallocated ESE track by calling ese_read() and unconditionally marking the request DASD_CQR_SUCCESS. dasd_eckd_ese_read() can return an error before it has zeroed the destination buffer: a failed sense-data parse or a current track outside the requested range both return early, leaving the destination pages untouched. The request is still completed successfully, so the block layer is handed stale / uninitialized memory instead of zeros.
Check the ese_read() return value and fail the request through the normal error path instead of forcing DASD_CQR_SUCCESS.
--- drivers/s390/block/dasd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/s390/block/dasd.c +4 -2
@@ -1744,8 +1744,10 @@ void dasd_int_handler(struct ccw_devicereturn ; }if (rq_data_dir (req) ==READ ) { - device->discipline->ese_read (cqr, irb); - cqr->status =DASD_CQR_SUCCESS ; +if (device->discipline->ese_read (cqr, irb)) + cqr->status =DASD_CQR_ERROR ; +else + cqr->status =DASD_CQR_SUCCESS ; cqr->stopclk = now;dasd_device_clear_timer (device);dasd_schedule_device_bh (device);
Greg Kroah-Hartman
maintainerPATCH 287/403 s390/dasd: Guard sysfs discipline callbacks against unallocated private data
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Haberland <[email protected]>
commit 2a1780f9fc2493bd34c418a0be6fc58943afcecf upstream.
Several sysfs show/store handlers call a discipline callback that dereferences device->private, either directly or through the DASD_DEFINE_ATTR() macro. During dasd_generic_set_online() the discipline is assigned before check_device() allocates device->private, so an unprivileged read of one of these world-readable attributes in that window dereferences a NULL pointer and panics.
Guard the dereference inside each callback that actually touches device->private.
--- drivers/s390/block/dasd_eckd.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-)
drivers/s390/block/dasd_eckd.c +38 -2
@@ -1492,6 +1492,8 @@ static void dasd_eckd_reset_path(structstruct dasd_eckd_private *private = device->private;unsigned long flags; +if (!private) +return ;if (!private->fcx_max_data) private->fcx_max_data =get_fcx_max_data (device);spin_lock_irqsave (get_ccwdev_lock (device->cdev), flags);
@@ -1647,6 +1649,9 @@ static int dasd_eckd_is_ese(struct dasd_ {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->vsq.vol_info.ese; }
@@ -1654,6 +1659,9 @@ static int dasd_eckd_ext_pool_id(struct {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->vsq.extent_pool_id; }
@@ -1667,6 +1675,9 @@ static int dasd_eckd_space_configured(ststruct dasd_eckd_private *private = device->private;int rc; +if (!private) +return 0 ; + rc =dasd_eckd_read_vol_info (device);return rc ? : private->vsq.space_configured;
@@ -1681,6 +1692,9 @@ static int dasd_eckd_space_allocated(strstruct dasd_eckd_private *private = device->private;int rc; +if (!private) +return 0 ; + rc =dasd_eckd_read_vol_info (device);return rc ? : private->vsq.space_allocated;
@@ -1690,6 +1704,9 @@ static int dasd_eckd_logical_capacity(st {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->vsq.logical_capacity; }
@@ -1832,7 +1849,11 @@ static int dasd_eckd_read_ext_pool_info(static int dasd_eckd_ext_size (struct dasd_device *device) {struct dasd_eckd_private *private = device->private; -struct dasd_ext_pool_sum eps = private->eps; +struct dasd_ext_pool_sum eps; + +if (!private) +return 0 ; + eps = private->eps;if (!eps.flags.extent_size_valid)return 0 ;
@@ -1848,6 +1869,9 @@ static int dasd_eckd_ext_pool_warn_thrsh {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->eps.warn_thrshld; }
@@ -1855,6 +1879,9 @@ static int dasd_eckd_ext_pool_cap_at_war {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->eps.flags.capacity_at_warnlevel; }
@@ -1865,6 +1892,9 @@ static int dasd_eckd_ext_pool_oos(struct {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->eps.flags.pool_oos; }
@@ -5948,8 +5978,11 @@ static int dasd_eckd_query_host_access(sstruct ccw1 *ccw;int rc; +if (!private) +return -ENODEV ; +/* not available for HYPER PAV alias devices */ -if (!device->block && private->lcu->pav ==HYPER_PAV ) +if (!device->block && private->lcu && private->lcu->pav ==HYPER_PAV )return -EOPNOTSUPP ;/* may not be supported by the storage server */
@@ -6814,6 +6847,9 @@ static int dasd_eckd_hpf_enabled(struct {struct dasd_eckd_private *private = device->private; +if (!private) +return 0 ; +return private->fcx_max_data ?1 :0 ; }
Greg Kroah-Hartman
maintainerPATCH 288/403 s390/dasd: Propagate partial completion length across ERP recovery
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Haberland <[email protected]>
commit 6fb5ba2e7e43173a3761e46f091070a8185efa14 upstream.
dasd_default_erp_postaction() copies the timing and device state from the finished ERP request back to the original request but drops proc_bytes. A request that was partially completed, an ESE read of a not-yet-allocated track returns fewer bytes than requested, and then recovered through the ERP chain loses its partial-completion length. __dasd_cleanup_cqr() then sees proc_bytes == 0 and completes the whole request instead of requeueing the remainder, silently returning zeroed data for the part that was never read.
Carry proc_bytes over to the original request like the other per-request state.
--- drivers/s390/block/dasd_erp.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/s390/block/dasd_erp.c +3 -0
@@ -122,6 +122,7 @@ struct dasd_ccw_req *dasd_default_erp_point success;unsigned long startclk, stopclk;struct dasd_device *startdev; +unsigned int proc_bytes;BUG_ON (cqr->refers ==NULL || cqr->function ==NULL );
@@ -129,6 +130,7 @@ struct dasd_ccw_req *dasd_default_erp_po startclk = cqr->startclk; stopclk = cqr->stopclk; startdev = cqr->startdev; + proc_bytes = cqr->proc_bytes;/* free all ERPs - but NOT the original cqr */ while (cqr->refers !=NULL ) {
@@ -146,6 +148,7 @@ struct dasd_ccw_req *dasd_default_erp_po cqr->startclk = startclk; cqr->stopclk = stopclk; cqr->startdev = startdev; + cqr->proc_bytes = proc_bytes;if (success) cqr->status =DASD_CQR_DONE ;else {
Greg Kroah-Hartman
maintainerPATCH 289/403 PCI: Fix 32-bit config write in Intel PCH Root Port MPC ACS quirk
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mohamad Raizudeen <[email protected]>
commit 23d7eed5974989de56273c964d7e510e4aad91e8 upstream.
pci_quirk_enable_intel_rp_mpc_acs() reads a 32-bit DWORD from the MPC register, sets bit 26 (INTEL_MPC_REG_IRBNCE), but it writes it back using pci_write_config_word().
Because bit 26 resides in the upper 16 bits of the 32-bit register, a 16-bit write drops the newly set bit. The quirk logs that it is enabling IRBNCE, but the hardware never actually receives the command.
Use pci_write_config_dword() to ensure the full 32-bit value is written back to the hardware.
--- drivers/pci/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/quirks.c +1 -1
@@ -5325,7 +5325,7 @@ static void pci_quirk_enable_intel_rp_mpif (!(mpc &INTEL_MPC_REG_IRBNCE )) {pci_info (dev,"Enabling MPC IRBNCE\n" ); mpc |=INTEL_MPC_REG_IRBNCE ; -pci_write_config_word (dev,INTEL_MPC_REG , mpc); +pci_write_config_dword (dev,INTEL_MPC_REG , mpc); } }
Greg Kroah-Hartman
maintainerPATCH 290/403 PCI: meson: Fix GPIO state while requesting PERST#
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ronald Claveau <[email protected]>
commit 40fb390cbcc11797c44c16dabdf763ec87643671 upstream.
Meson devicetree defines the PERST# GPIO as 'reset' GPIO. Commit 4d3186a525b3 ("PCI: amlogic: Fix reset assertion via gpio descriptor") inverted the PERST# assertion logic to use proper GPIO descriptor semantics and moved the polarity configuration to the device tree as GPIO_ACTIVE_LOW. However, the initial PERST# GPIO state "GPIOD_OUT_LOW" was not updated accordingly.
This results in the enumeration failure of the endpoint devices as PERST# would get deasserted while requesting the GPIO even before power and REFCLK becomes stable.
Without this fix:
ahci 0000:01:00.0: enabling device (0000 -> 0002) ahci 0000:01:00.0: SSS flag set, parallel bus scan disabled ahci 0000:01:00.0: Controller reset failed (0xffffffff) ahci 0000:01:00.0: probe with driver ahci failed with error -5
With this fix:
ahci 0000:01:00.0: enabling device (0000 -> 0002)
ahci 0000:01:00.0: AHCI vers 0001.0300, 32 command slots, 6 Gbps, SATA mode ahci 0000:01:00.0: 1/1 ports implemented (port mask 0x1)
ahci 0000:01:00.0: flags: 64bit ncq led clo only pio ccc
Change the GPIO request flag from GPIOD_OUT_LOW to GPIOD_OUT_HIGH to get the right behaviour.
--- drivers/pci/controller/dwc/pci-meson.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/controller/dwc/pci-meson.c +1 -1
@@ -421,7 +421,7 @@ static int meson_pcie_probe(struct platfreturn PTR_ERR (mp->phy); } - mp->reset_gpio =devm_gpiod_get (dev,"reset" ,GPIOD_OUT_LOW ); + mp->reset_gpio =devm_gpiod_get (dev,"reset" ,GPIOD_OUT_HIGH );if (IS_ERR (mp->reset_gpio)) {dev_err (dev,"get reset gpio failed\n" );return PTR_ERR (mp->reset_gpio);
Greg Kroah-Hartman
maintainerPATCH 291/403 PCI: plda: Fix use-after-free of event IRQs during teardown
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Tariq <[email protected]>
commit 26b73bae01d6eb81a4a38f36101812f20b2639de upstream.
plda_pcie_irq_domain_deinit() removes pcie->event_domain via irq_domain_remove(), but the per-event IRQs mapped from that domain are requested with devm_request_irq() in plda_init_interrupts(). The actual free_irq() for a devm-managed IRQ is deferred by devres until after the calling probe()/remove() function returns.
This means irq_domain_remove() can free the domain's internal data before the deferred free_irq() for IRQs still mapped into it has run. When devres later processes that deferred cleanup, it can end up dereferencing the already-freed domain.
Free each event IRQ explicitly with devm_free_irq() before removing the domain. This triggers the free immediately and removes the IRQ from the devres tracking list, so devres will not attempt to free it a second time later.
Also dispose of the event, INTx, and MSI IRQ mappings with irq_dispose_mapping() before their owning domains are removed.
Finally, guard the calls to irq_set_chained_handler_and_data() for pcie->irq, pcie->msi_irq, and pcie->intx_irq so they only run when those fields hold a valid (>0) IRQ number.
This is a pre-existing issue, flagged by automated review during work on an earlier, unrelated patch to this driver.
Build-tested and boot-tested on StarFive VisionFive v1.2A board
--- drivers/pci/controller/plda/pcie-plda-host.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-)
drivers/pci/controller/plda/pcie-plda-host.c +21 -3
@@ -561,9 +561,27 @@ EXPORT_SYMBOL_GPL(plda_pcie_setup_iomemsstatic void plda_pcie_irq_domain_deinit (struct plda_pcie_rp *pcie) { -irq_set_chained_handler_and_data (pcie->irq,NULL ,NULL ); -irq_set_chained_handler_and_data (pcie->msi_irq,NULL ,NULL ); -irq_set_chained_handler_and_data (pcie->intx_irq,NULL ,NULL ); +u32 i, event_irq; + +if (pcie->irq >0 ) +irq_set_chained_handler_and_data (pcie->irq,NULL ,NULL ); +if (pcie->msi_irq >0 ) +irq_set_chained_handler_and_data (pcie->msi_irq,NULL ,NULL ); +if (pcie->intx_irq >0 ) +irq_set_chained_handler_and_data (pcie->intx_irq,NULL ,NULL ); + +for_each_set_bit (i, &pcie->events_bitmap, pcie->num_events) { + event_irq =irq_find_mapping (pcie->event_domain, i); +if (event_irq) { +devm_free_irq (pcie->dev, event_irq, pcie); +irq_dispose_mapping (event_irq); + } + } + +if (pcie->intx_irq) +irq_dispose_mapping (pcie->intx_irq); +if (pcie->msi_irq) +irq_dispose_mapping (pcie->msi_irq);irq_domain_remove (pcie->msi.msi_domain);irq_domain_remove (pcie->msi.dev_domain);
Greg Kroah-Hartman
maintainerPATCH 292/403 PCI: plda: Fix IRQ domain leaks in the error paths of plda_init_interrupts()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Tariq <[email protected]>
commit 19a30bbb6477bfd7e3109b7a2943e6597ee9de37 upstream.
plda_init_interrupts() initializes IRQ domains and creates IRQ mapping but does not unwind them when later step fails.
If platform_get_irq() or either irq_create_mapping() fails in plda_init_interrupts(), the domains are never deinitialized. If irq_create_mapping() fails, port->intx_irq stays initialized.
Hence, remove the IRQ domains in the error path by calling plda_pcie_irq_domain_deinit().
Since plda_pcie_irq_domain_deinit() now disposes of the intx_irq and msi_irq mappings itself before removing their domains, the msi_irq mapping failure path can go directly to err_irq_domain_deinit instead of disposing of port->intx_irq separately first.
This issue was found by automated review of sashiko-bot
--- drivers/pci/controller/plda/pcie-plda-host.c | 28 ++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-)
drivers/pci/controller/plda/pcie-plda-host.c +21 -7
@@ -421,6 +421,8 @@ static int plda_pcie_init_irq_domains(streturn plda_allocate_msi_domains (port); } +static void plda_pcie_irq_domain_deinit (struct plda_pcie_rp *pcie); +int plda_init_interrupts (struct platform_device *pdev,struct plda_pcie_rp *port,const struct plda_event *event)
@@ -442,14 +444,17 @@ int plda_init_interrupts(struct platform } port->irq =platform_get_irq (pdev,0 ); -if (port->irq <0 ) -return -ENODEV ; +if (port->irq <0 ) { + ret = -ENODEV ; + goto err_irq_domain_deinit; + }for_each_set_bit (i, &port->events_bitmap, port->num_events) { event_irq =irq_create_mapping (port->event_domain, i);if (!event_irq) {dev_err (dev,"failed to map hwirq %d\n" , i); -return -ENXIO ; + ret = -ENXIO ; + goto err_irq_domain_deinit; }if (event->request_event_irq)
@@ -461,7 +466,7 @@ int plda_init_interrupts(struct platformif (ret) {dev_err (dev,"failed to request IRQ %d\n" , event_irq); -return ret; + goto err_irq_domain_deinit; } }
@@ -469,7 +474,8 @@ int plda_init_interrupts(struct platform event->intx_event);if (!port->intx_irq) {dev_err (dev,"failed to map INTx interrupt\n" ); -return -ENXIO ; + ret = -ENXIO ; + goto err_irq_domain_deinit; }/* Plug the INTx chained handler */
@@ -477,8 +483,11 @@ int plda_init_interrupts(struct platform port->msi_irq =irq_create_mapping (port->event_domain, event->msi_event); -if (!port->msi_irq) -return -ENXIO ; +if (!port->msi_irq) { +dev_err (dev,"failed to map MSI interrupt\n" ); + ret = -ENXIO ; + goto err_irq_domain_deinit; + }/* Plug the MSI chained handler */ irq_set_chained_handler_and_data (port->msi_irq, plda_handle_msi, port);
@@ -487,6 +496,11 @@ int plda_init_interrupts(struct platformirq_set_chained_handler_and_data (port->irq, plda_handle_event, port);return 0 ; + +err_irq_domain_deinit: +plda_pcie_irq_domain_deinit (port); + +return ret; }EXPORT_SYMBOL_GPL (plda_init_interrupts);
Greg Kroah-Hartman
maintainerPATCH 293/403 PCI: Add ACS quirk for Pericom PI7C9X2G608 switches [12d8:2608]
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tim Harvey <[email protected]>
commit 062fb7f816439da6bf3860386889343482a66bd4 upstream.
The Pericom PI7C9X2G608 6-port Gen2 PCIe switch is also affected by the PI7C9X2G errata per the errata document:
E2: ACS P2P Request Redirect Is Not Functional
Apply the same quirk to this PCI ID as well to apply the workaround required if using ACS.
--- drivers/pci/quirks.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/pci/quirks.c +4 -0
@@ -6204,6 +6204,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_P pci_fixup_pericom_acs_store_forward);DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_PERICOM ,0xb404 , pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_ENABLE (PCI_VENDOR_ID_PERICOM ,0x2608 , + pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_RESUME (PCI_VENDOR_ID_PERICOM ,0x2608 , + pci_fixup_pericom_acs_store_forward);static void nvidia_ion_ahci_fixup (struct pci_dev *pdev) {
Greg Kroah-Hartman
maintainerPATCH 294/403 PCI/sysfs: Fix read byte order in pci_read_legacy_io()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit 5b95212de6dcd7e0275cea7f894fe7226c7d9f29 upstream.
pci_read_legacy_io() passes the sysfs buffer directly to pci_legacy_read():
return pci_legacy_read(bus, off, (u32 *)buf, count);
The PowerPC implementation stores the result as a native-endian integer:
*((u16 *)val) = in_le16(addr);
On big-endian PowerPC this stores the bytes in the wrong order, so a 2-byte read of a device register returns different bytes than two 1-byte reads at the same addresses. The same applies to 4-byte reads. On little-endian the native byte order already matches PCI I/O port byte order, so the conversion is a no-op.
Thus, let pci_legacy_read() store into a local u32 variable, then copy the I/O port value to the sysfs buffer using put_unaligned_le16() and put_unaligned_le32() for the 2 and 4 byte cases, converting from the native integer to little-endian byte order matching PCI I/O port space.
No changes are needed for the Alpha platform.
The legacy_io file is root-only and exists only on Alpha and PowerPC, the two architectures that define HAVE_PCI_LEGACY.
--- drivers/pci/pci-sysfs.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
drivers/pci/pci-sysfs.c +19 -1
@@ -882,12 +882,30 @@ static ssize_t pci_read_legacy_io(struct loff_t off, size_t count) {struct pci_bus *bus =to_pci_bus (kobj_to_dev (kobj)); +u32 val =0 ; +int ret;/* Only support 1, 2 or 4 byte accesses */ if (count !=1 && count !=2 && count !=4 )return -EINVAL ; -return pci_legacy_read (bus, off, (u32 *)buf, count); + ret =pci_legacy_read (bus, off, &val, count); +if (ret <0 ) +return ret; + +switch (count) { +case 1 : + buf[0 ] = *(u8 *)&val; +break ; +case 2 : +put_unaligned_le16 (*(u16 *)&val, buf); +break ; +case 4 : +put_unaligned_le32 (val, buf); +break ; + } + +return ret; }/**
Greg Kroah-Hartman
maintainerPATCH 295/403 PCI/sysfs: Avoid spurious runtime PM wakeup on config space accesses
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit b14b2bab88d7099ab4447560cbe4b40945e5c069 upstream.
Currently, the boundary checks in pci_read_config() and pci_write_config() reject only offsets beyond the effective configuration space size.
An access at an offset exactly equal to that size passes the check, has its length clamped to zero, and then invokes pci_config_pm_runtime_get() and pci_config_pm_runtime_put() around transfer blocks that do nothing.
This is a problem because pci_config_pm_runtime_get() synchronously resumes the upstream bridge through pm_runtime_get_sync() and resumes the device itself through pm_runtime_resume() when it is in D3cold, only for the handler to return zero immediately afterwards. Such a spurious wakeup wastes power and adds needless resume latency.
The sysfs core already clamps accesses against the attribute size set through the bin_size() callback, which reports either 256 or 4096 bytes. As such, the affected accesses are reads at offset 64 (or 128 for CardBus devices) through files opened without CAP_SYS_ADMIN, and reads and writes at the exact configuration space size on devices where a quirk sets a non-standard size.
Reject accesses at the boundary offset as well, so they return early before any runtime PM involvement, matching the procfs implementations in proc_bus_pci_read() and proc_bus_pci_write().
The value returned to userspace at these offsets remains zero, so the change is not visible to userspace.
--- drivers/pci/pci-sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/pci/pci-sysfs.c +2 -2
@@ -698,7 +698,7 @@ static ssize_t pci_read_config(struct fielse if (dev->hdr_type ==PCI_HEADER_TYPE_CARDBUS ) size =128 ; -if (off > size) +if (off >= size)return 0 ;if (off + count > size) { size -= off;
@@ -779,7 +779,7 @@ static ssize_t pci_write_config(struct fadd_taint (TAINT_USER ,LOCKDEP_STILL_OK ); } -if (off > dev->cfg_size) +if (off >= dev->cfg_size)return 0 ;if (off + count > dev->cfg_size) { size = dev->cfg_size - off;
Greg Kroah-Hartman
maintainerPATCH 296/403 PCI/ASPM: Avoid L0s for Realtek RTS525A
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Lee <[email protected]>
commit ec3d987fcaf92516d13ee18c305c82281557046d upstream.
The Realtek RTS525A PCIe card reader reports an AER Correctable Replay Timer Timeout storm when ASPM L0s is enabled on its link. On an affected HP ZBook Power 16 inch G11, the Root Port received tens of millions of AER interrupts from the RTS525A even when the rtsx_pci driver was blacklisted and the endpoint was not enabled by a driver.
For example:
pcieport 0000:00:1c.6: AER: Multiple Correctable error message received from 0000:58:00.0 rtsx_pci 0000:58:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID)
rtsx_pci 0000:58:00.0: device [10ec:525a] error status/mask=00001000/00006000
rtsx_pci 0000:58:00.0: [12] Timeout
pcieport 0000:00:1c.6: AER: Correctable error message received from 0000:58:00.0
Testing with OS-native AER control showed that disabling only L0s on the RTS525A link stops new AER interrupt and counter growth while leaving L1 enabled. Disabling L1, L1 substates, or Clock PM alone did not stop the storm.
Prevent the broken L0s configuration by removing L0s from the RTS525A advertised ASPM capability. This avoids enabling the non-working ASPM state instead of masking the resulting AER Replay Timer Timeout reports.
--- drivers/pci/quirks.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/pci/quirks.c +3 -0
@@ -2503,6 +2503,9 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INDECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_INTEL ,0x10f4 , quirk_disable_aspm_l0s);DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_INTEL ,0x1508 , quirk_disable_aspm_l0s); +/* Realtek RTS525A generates a Replay Timer Timeout storm when L0s is enabled. */ +DECLARE_PCI_FIXUP_HEADER (PCI_VENDOR_ID_REALTEK ,0x525a , quirk_disable_aspm_l0s); +static void quirk_disable_aspm_l0s_l1 (struct pci_dev *dev) {pci_info (dev,"Disabling ASPM L0s/L1\n" );
Re: [PATCH 6.12 296/403] PCI/ASPM: Avoid L0s for Realtek RTS525A
Hi Greg/Sasha,
Quoted 45 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Max Lee <[email protected]>
commit
ec3d987fcaf92516d13ee18c305c82281557046dupstream.The Realtek RTS525A PCIe card reader reports an AER Correctable Replay Timer Timeout storm when ASPM L0s is enabled on its link. On an affected HP ZBook Power 16 inch G11, the Root Port received tens of millions of AER interrupts from the RTS525A even when the
rtsx_pcidriver was blacklisted and the endpoint was not enabled by a driver.For example:
pcieport 0000:00:1c.6: AER: Multiple Correctable error message received from 0000:58:00.0
rtsx_pci0000:58:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID)
rtsx_pci0000:58:00.0: device [10ec:525a] error status/mask=00001000/00006000
rtsx_pci0000:58:00.0: [12] Timeout
pcieport 0000:00:1c.6: AER: Correctable error message received from 0000:58:00.0Testing with OS-native AER control showed that disabling only L0s on the RTS525A link stops new AER interrupt and counter growth while leaving L1 enabled. Disabling L1, L1 substates, or Clock PM alone did not stop the storm.
Prevent the broken L0s configuration by removing L0s from the RTS525A advertised ASPM capability. This avoids enabling the non-working ASPM state instead of masking the resulting AER Replay Timer Timeout reports.
Signed-off-by Max Lee <[email protected]>Signed-off-by Bjorn Helgaas <[email protected]>Reviewed-by Lukas Wunner <[email protected]>Reviewed-by Manivannan Sadhasivam <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/pci/quirks.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/pci/quirks.c DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN · line 2508
Quoted 6 lines
drivers/pci/quirks.c +3
-0@@ -2503,6 +2503,9 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INDECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_INTEL ,0x10f4 , quirk_disable_aspm_l0s);DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_INTEL ,0x1508 , quirk_disable_aspm_l0s); +/* Realtek RTS525A generates a Replay Timer Timeout storm when L0s is enabled. */ +DECLARE_PCI_FIXUP_HEADER (PCI_VENDOR_ID_REALTEK ,0x525a , quirk_disable_aspm_l0s); +
^^^^
I have run an AI-assisted backport review and spotted an issue.
Upstream commit: ec3d987fcaf9 ("PCI/ASPM: Avoid L0s for Realtek RTS525A") disables L0s before ASPM initialization using cached capabilities.
On 6.12.y, the HEADER quirk calls:
pci_disable_link_state(dev, PCIE_LINK_STATE_L0S);
At this point link_state does not exist, so the call returns -EINVAL and L0s remains enabled.
The required support comes from commit: 4495bffd86ba, commit:575b98e39d81, and commit: 30579eebba6a. None is present in 6.12.y.
Without those prerequisites or a 6.12-specific fix, I would suggest dropping commit: dbccca7f5d2f ("PCI/ASPM: Avoid L0s for Realtek RTS525A"). Thoughts?
thanks,
Harshit
Quoted 6 lines
drivers/pci/quirks.c +0
-0static void quirk_disable_aspm_l0s_l1 (struct pci_dev *dev) {pci_info (dev,"Disabling ASPM L0s/L1\n" );
Sasha Levin
maintainerRe: [PATCH 6.12 296/403] PCI/ASPM: Avoid L0s for Realtek RTS525A
Quoted 3 lines
Without those prerequisites or a 6.12-specific fix, I would suggest dropping commit:
dbccca7f5d2f("PCI/ASPM: Avoid L0s for Realtek RTS525A"). Thoughts?
Dropped from 6.12, 6.6, 6.1, 5.15, and 5.10. Kept in 7.2 and 6.18, which already carry the three prerequisites (4495bffd86ba, 575b98e39d81, 30579eebba6a). Thanks for the report.
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 297/403 PCI/DPC: Allow DPC on all Downstream Ports when OS controls AER
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Darshit Shah <[email protected]>
commit 97ca178c899d0049210d325b123ed024eb5ac000 upstream.
PCIe r7.0, sec 6.2.11, "Implementation Note: Determination of DPC Control", recommends that "... operating systems always link control of DPC to the control of Advanced Error Reporting."
Any PCIe device may advertise AER, but only Root Ports and Root Complex Event Collectors can generate AER interrupts, so the AER driver only binds to RPs and RCECs.
Any Root Port or Switch Downstream Port may advertise Downstream Port Containment (DPC), but previously the DPC driver was limited to devices the AER driver could bind to, i.e., only RPs that advertised AER.
Since any Port with DPC can generate DPC interrupts, allow the DPC driver to bind to such a Port as long as the OS controls AER, regardless of whether the AER driver binds to it.
--- drivers/pci/pcie/portdrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/pcie/portdrv.c +1 -1
@@ -264,7 +264,7 @@ static int get_port_device_capability(st*/ if (pci_find_ext_capability (dev,PCI_EXT_CAP_ID_DPC ) &&pci_aer_available () && - (pcie_ports_dpc_native || (services &PCIE_PORT_SERVICE_AER ))) + (pcie_ports_dpc_native || host->native_aer)) services |=PCIE_PORT_SERVICE_DPC ; if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
Greg Kroah-Hartman
maintainerPATCH 298/403 PCI/MSI: Enable memory decoding before restoring MSI-X messages
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Farhan Ali <[email protected]>
commit 231c7a57d19304beb0931e6cbe3a4929daf49747 upstream.
The current MSI-X restoration path assumes the Command register Memory bit is enabled when writing MSI-X messages. But it's possible the last saved and restored state of a device may not have the Memory bit enabled, even if a device driver later enables Memory bit and MSI-X. Attempting to access Memory space without Memory bit enabled can lead to Unsupported Request (UR) from the device. Fix this by enabling Memory bit and restore it afterwards.
--- drivers/pci/msi/msi.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/pci/msi/msi.c +10 -0
@@ -862,6 +862,7 @@ void __pci_restore_msix_state(struct pci {struct msi_desc *entry;bool write_msg; +u16 cmd;if (!dev->msix_enabled)return ;
@@ -871,6 +872,14 @@ void __pci_restore_msix_state(struct pcipci_msix_clear_and_set_ctrl (dev,0 ,PCI_MSIX_FLAGS_ENABLE |PCI_MSIX_FLAGS_MASKALL ); +/* +* The restored device state may not have Memory Space enabled. +* Since the MSI-X Table and PBA are in Memory Space, enable it +* while restoring them. +*/ +pci_read_config_word (dev,PCI_COMMAND , &cmd); +pci_write_config_word (dev,PCI_COMMAND , cmd |PCI_COMMAND_MEMORY ); + write_msg =arch_restore_msi_irqs (dev);msi_lock_descs (&dev->dev);
@@ -881,6 +890,7 @@ void __pci_restore_msix_state(struct pci }msi_unlock_descs (&dev->dev); +pci_write_config_word (dev,PCI_COMMAND , cmd);pci_msix_clear_and_set_ctrl (dev,PCI_MSIX_FLAGS_MASKALL ,0 ); }
Greg Kroah-Hartman
maintainerPATCH 299/403 PCI/proc: Avoid spurious runtime PM wakeup on config space accesses
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit 4ff664a81d729b37f2eb65de80a670abfb61c9a0 upstream.
Currently, proc_bus_pci_read() and proc_bus_pci_write() do not return early for zero-length configuration space accesses at valid offsets.
Such an access invokes pci_config_pm_runtime_get() and pci_config_pm_runtime_put() around transfer blocks that do nothing.
This is a problem because pci_config_pm_runtime_get() synchronously resumes the upstream bridge through pm_runtime_get_sync(), and resumes the device itself through pm_runtime_resume() when it is in D3cold, only for the handler to return zero immediately afterwards. Such a spurious wakeup wastes power and adds needless resume latency.
The sysfs core already returns early for in-range zero-length binary attribute accesses before pci_read_config() or pci_write_config() is invoked. In contrast, the VFS forwards zero-length requests to the procfs callbacks, where they continue into runtime PM handling.
Return early from proc_bus_pci_read() and proc_bus_pci_write() when nbytes is zero, before any runtime PM involvement.
The value returned to userspace at these offsets remains zero, so the change is not visible to userspace.
--- drivers/pci/proc.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/pci/proc.c +6 -0
@@ -45,6 +45,9 @@ static ssize_t proc_bus_pci_read(structelse size =64 ; +if (!nbytes) +return 0 ; +if (pos >= size)return 0 ;if (nbytes >= size)
@@ -121,6 +124,9 @@ static ssize_t proc_bus_pci_write(structif (ret)return ret; +if (!nbytes) +return 0 ; +if (pos >= size)return 0 ;if (nbytes >= size)
Greg Kroah-Hartman
maintainerPATCH 300/403 PCI/proc: Use file_ns_capable() when checking config space read access
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit f82f53e75eff382fc8f56b73279b54f7cf5a5c65 upstream.
proc_bus_pci_read() decides how much of the config space is readable based on capable(CAP_SYS_ADMIN), which checks the credentials of the task calling read(), not the credentials of the process that opened the file.
The sysfs equivalent, pci_read_config(), has checked the credentials of the opening process since commit de139a339395 ("pci: check caps from sysfs file open to read device dependent config space"), so a privileged process can open the config space file and pass the file descriptor to an unprivileged process (for example, a process running a KVM guest with an assigned device), which can then read the entire config space. The check was subsequently routed through the LSM framework in commit 47970b1b2aa6 ("pci:
use security_capable() when checking capablities during config space read") and converted to the dedicated helper in commit ab0fa82b2df9 ("pci-sysfs:
use proper file capability helper function").
Thus, the two interfaces check the same capability against different credentials. Checking the credentials of the task calling read() makes the outcome depend on who reads rather than who opened, so the restriction is bypassed whenever a more privileged process reads through the descriptor. Checking the credentials recorded in file->f_cred settles the decision at open() time and ties it to the file, where it cannot change with the caller.
Use file_ns_capable() to check CAP_SYS_ADMIN against the credentials in effect when the file was opened, bringing the procfs interface in line with the sysfs behaviour.
As a result, a file descriptor opened by a privileged process and passed to an unprivileged one now allows the entire config space to be read through procfs, matching sysfs.
--- drivers/pci/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/pci/proc.c +1 -1
@@ -38,7 +38,7 @@ static ssize_t proc_bus_pci_read(struct* undefined locations (think of Intel PIIX4 as a typical example). */ -if (capable (CAP_SYS_ADMIN )) +if (file_ns_capable (file, &init_user_ns,CAP_SYS_ADMIN )) size = dev->cfg_size;else if (dev->hdr_type ==PCI_HEADER_TYPE_CARDBUS ) size =128 ;
Greg Kroah-Hartman
maintainerPATCH 301/403 PCI/proc: Warn on writes to kernel-exclusive config space regions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Wilczyński <[email protected]>
commit 3359e044d597dd5344f17613e4be6b6e12067f60 upstream.
Currently, a driver can claim a region of a device's config space as exclusive using pci_request_config_region_exclusive(), after which a write to that region originating from user space is expected to emit a warning and taint the kernel. The check is advisory only, as the write itself is still allowed to proceed.
Since commit 278294798ac9 ("PCI: Allow drivers to request exclusive config regions"), the sysfs config space attribute performs this check in pci_write_config(), but the procfs interface was never updated. A write performed through /proc/bus/pci/BB/DD.F therefore bypasses the detection entirely, even though both interfaces offer the same level of access.
Add the same resource_is_exclusive() check to proc_bus_pci_write().
--- drivers/pci/proc.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/pci/proc.c +8 -0
@@ -14,6 +14,8 @@#include <linux/capability.h> #include <linux/uaccess.h> #include <linux/security.h> +#include <linux/panic.h> +#include <linux/sched.h> #include <asm/byteorder.h> #include "pci.h"
@@ -127,6 +129,12 @@ static ssize_t proc_bus_pci_write(structif (!nbytes)return 0 ; +if (resource_is_exclusive (&dev->driver_exclusive_resource, pos, nbytes)) { +pci_warn_once (dev,"%s: Unexpected write to kernel-exclusive config offset %x" , + current->comm, pos); +add_taint (TAINT_USER ,LOCKDEP_STILL_OK ); + } +if (pos >= size)return 0 ;if (nbytes >= size)
Greg Kroah-Hartman
maintainerPATCH 302/403 iommu/amd: Put PCI device after handling PPR faults
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuai Xue <[email protected]>
commit af3b69b16383fbc8fe5f61b5b0150d2e41ede71f upstream.
iommu_call_iopf_notifier() looks up the requester with pci_get_domain_bus_and_slot(), which returns a PCI device with its reference count incremented.
Neither the successful iommu_report_device_fault() path nor the abort path drops that reference, so every handled PPR request leaks a PCI device reference.
This is the same ownership rule that was fixed for the old iommu_v2 ppr_notifier() path by commit 6cf0981c2233 ("iommu/amd: Fix pci device refcount leak in ppr_notifier()"), but iommu_call_iopf_notifier() was added later as a separate PPR/IOPF notifier path.
Drop the PCI device reference after handling the PPR entry.
--- drivers/iommu/amd/ppr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/iommu/amd/ppr.c +2 -1
@@ -151,7 +151,7 @@ static void iommu_call_iopf_notifier(struct amd_iommu *iommu, u64 *raw)/* Submit event */ iommu_report_device_fault (&pdev->dev, &event); - +pci_dev_put (pdev);return ; out:
@@ -159,6 +159,7 @@ static void iommu_call_iopf_notifier(struct amd_iommu *iommu, u64 *raw)amd_iommu_complete_ppr (&pdev->dev,PPR_PASID (raw[0 ]),IOMMU_PAGE_RESP_FAILURE ,PPR_TAG (raw[0 ]) &0x1FF ); +pci_dev_put (pdev); }void amd_iommu_poll_ppr_log (struct amd_iommu *iommu)
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 303/403 iommu/sva: Set handle->dev before the SVA handle is visible
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuai Xue <[email protected]>
commit 530f8f9c3546cb3ebee1b135375aaee08a073ebb upstream.
iommu_attach_device_pasid() installs the new SVA attach handle in the group PASID lookup before iommu_sva_bind_device() returns. A concurrent bind can therefore find and reuse the same handle after iommu_sva_lock is dropped.
handle->dev was initialized after dropping iommu_sva_lock. This leaves a window where a racing bind can return a handle whose dev pointer is still NULL. A subsequent iommu_sva_unbind_device() can then dereference it via handle->dev->iommu_group.
Initialize handle->dev before releasing iommu_sva_lock so any visible SVA handle is fully initialized.
--- drivers/iommu/iommu-sva.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/iommu-sva.c +1 -1
@@ -142,8 +142,8 @@ struct iommu_sva *iommu_sva_bind_device( out:refcount_set (&handle->users,1 ); -mutex_unlock (&iommu_sva_lock); handle->dev = dev; +mutex_unlock (&iommu_sva_lock);return handle; out_free_domain:
Greg Kroah-Hartman
maintainerPATCH 304/403 iommu/arm-smmu-v3: Manage teardown with devm
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shameer Kolothum <[email protected]>
commit 2bd22a0d40503a65d243b011de146603c8ce1cbc upstream.
arm_smmu_device_remove() manually frees the IOPF queue, destroys the vmid_map and disables the device, while the IRQs and queues are devm managed. devm unwinds only after remove() returns, so the cleanup runs in the wrong order. The IOPF queue is freed before the event-queue IRQ whose handler uses it.
Manage all of it with devm so the unwind order is correct. Free the IOPF queue and vmid_map via devm actions, and disable the device from one registered after arm_smmu_device_reset().
This is also a prerequisite for fixing a Tegra241 CMDQV CMD_SYNC use-after-free in the subsequent patch.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 56 ++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 16 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +40 -16
@@ -3619,6 +3619,20 @@ int arm_smmu_cmdq_init(struct arm_smmu_dreturn 0 ; } +static void arm_smmu_free_iopf_action (void *data) +{ +struct iopf_queue *queue = data; + +iopf_queue_free (queue); +} + +static void arm_smmu_destroy_vmid_map (void *data) +{ +struct ida *ida = data; + +ida_destroy (ida); +} +static int arm_smmu_init_queues (struct arm_smmu_device *smmu) {int ret;
@@ -3646,6 +3660,11 @@ static int arm_smmu_init_queues(struct a smmu->evtq.iopf =iopf_queue_alloc (dev_name (smmu->dev));if (!smmu->evtq.iopf)return -ENOMEM ; + ret =devm_add_action_or_reset (smmu->dev, + arm_smmu_free_iopf_action, + smmu->evtq.iopf); +if (ret) +return ret; }/* priq */
@@ -3724,7 +3743,8 @@ static int arm_smmu_init_strtab(struct aida_init (&smmu->vmid_map); -return 0 ; +return devm_add_action_or_reset (smmu->dev, arm_smmu_destroy_vmid_map, + &smmu->vmid_map); }static int arm_smmu_init_structures (struct arm_smmu_device *smmu)
@@ -3937,6 +3957,13 @@ static int arm_smmu_device_disable(strucreturn ret; } +static void arm_smmu_disable_action (void *data) +{ +struct arm_smmu_device *smmu = data; + +arm_smmu_device_disable (smmu); +} +static void arm_smmu_write_strtab (struct arm_smmu_device *smmu) {struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
@@ -4646,7 +4673,7 @@ static int arm_smmu_device_probe(struct/* Initialise in-memory data structures */ ret =arm_smmu_init_structures (smmu);if (ret) - goto err_free_iopf; +return ret;/* Record our private device structure */ platform_set_drvdata (pdev, smmu);
@@ -4656,30 +4683,30 @@ static int arm_smmu_device_probe(struct/* Reset the device */ ret =arm_smmu_device_reset (smmu); +if (ret) { +arm_smmu_device_disable (smmu); +return ret; + } + +/* Register last so it unwinds first, while the CMDQ is still up. */ + ret =devm_add_action_or_reset (smmu->dev, arm_smmu_disable_action, smmu);if (ret) - goto err_disable; +return ret;/* And we're up. Go go go! */ ret =iommu_device_sysfs_add (&smmu->iommu, dev,NULL ,"smmu3.%pa" , &ioaddr);if (ret) - goto err_disable; +return ret; ret =iommu_device_register (&smmu->iommu, &arm_smmu_ops, dev);if (ret) {dev_err (dev,"Failed to register iommu\n" ); - goto err_free_sysfs; +iommu_device_sysfs_remove (&smmu->iommu); +return ret; }return 0 ; - -err_free_sysfs: -iommu_device_sysfs_remove (&smmu->iommu); -err_disable: -arm_smmu_device_disable (smmu); -err_free_iopf: -iopf_queue_free (smmu->evtq.iopf); -return ret; }static void arm_smmu_device_remove (struct platform_device *pdev)
@@ -4688,9 +4715,6 @@ static void arm_smmu_device_remove(struciommu_device_unregister (&smmu->iommu);iommu_device_sysfs_remove (&smmu->iommu); -arm_smmu_device_disable (smmu); -iopf_queue_free (smmu->evtq.iopf); -ida_destroy (&smmu->vmid_map); }static void arm_smmu_device_shutdown (struct platform_device *pdev)
Greg Kroah-Hartman
maintainerPATCH 305/403 iommu/vt-d: Fix no_iommu to disable platform opt-in
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Tian <[email protected]>
commit 219cc978d69ce9b538d0d73936c569d4ca5b0a24 upstream.
If user explicitly requests to disable iommu (via "iommu=off" or "intel_iommu=off"), there is no reason to force enabling it due to platform opt-in (for external-facing devices). User should be aware of any security implication of doing so.
"intel_iommu=off" implements this policy by setting no_platform_optin to skip platform opt-in in platform_optin_force_iommu().
However, "iommu=off" (no_iommu=1) doesn't set no_platform_optin hence is broken in this aspect:
- detect_intel_iommu() doesn't request ACS if no_iommu=1
- platform_optin_force_iommu() forces iommu on if external-facing devices exist and no_platform_optin is not set
This leads to a bad configuration with ACS disabled while DMA remapping is enabled.
Instead of setting no_platform_optin (will soon be removed) for no_iommu=1, directly check no_iommu in platform_optin_force_iommu().
--- drivers/iommu/intel/iommu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/iommu/intel/iommu.c +3 -3
@@ -3156,10 +3156,11 @@ static bool has_external_pci(void)static int __init platform_optin_force_iommu (void ) { -if (!dmar_platform_optin () || no_platform_optin || !has_external_pci ()) +if (no_iommu || !dmar_platform_optin () || no_platform_optin || + !has_external_pci ())return 0 ; -if (no_iommu || dmar_disabled) +if (dmar_disabled)pr_info ("Intel-IOMMU force enabled due to platform opt in\n" );/*
@@ -3170,7 +3171,6 @@ static int __init platform_optin_force_iiommu_set_default_passthrough (false); dmar_disabled =0 ; - no_iommu =0 ;return 1 ; }
Greg Kroah-Hartman
maintainerPATCH 306/403 iommu/vt-d: Force requesting ACS when tboot is enabled
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Tian <[email protected]>
commit 607432b2618b61df81134be0ef2562b8300c1216 upstream.
Currently the conditions of requesting ACS in detect_intel_iommu() don't include tboot, leading to a possible misconfiguration with ACS disabled (e.g. due to user opts) while iommu is later forced on by tboot_force_iommu().
Fix it by checking tboot in detect_intel_iommu().
--- drivers/iommu/intel/dmar.c | 15 +++++++++++++-- drivers/iommu/intel/iommu.c | 2 +- drivers/iommu/intel/iommu.h | 2 ++ 3 files changed, 16 insertions(+), 3 deletions(-)
drivers/iommu/intel/dmar.c +13 -2
@@ -915,6 +915,18 @@ dmar_validate_one_drhd(struct acpi_dmar_return 0 ; } +static bool dmar_required (void ) +{ +/* tboot supersedes any user/platform opt */ +if (!intel_iommu_tboot_noforce &&tboot_enabled ()) +return true; + +if (!no_iommu && (!dmar_disabled ||dmar_platform_optin ())) +return true; + +return false; +} +void __init detect_intel_iommu (void ) {int ret;
@@ -928,8 +940,7 @@ void __init detect_intel_iommu(void)if (!ret) ret =dmar_walk_dmar_table ((struct acpi_table_dmar *)dmar_tbl, &validate_drhd_cb); -if (!ret && !no_iommu && !iommu_detected && - (!dmar_disabled ||dmar_platform_optin ())) { +if (!ret && !iommu_detected &&dmar_required ()) { iommu_detected =1 ;/* Make sure ACS will be enabled */ pci_request_acs ();
drivers/iommu/intel/iommu.c +1 -1
@@ -63,7 +63,7 @@ static int rwbf_quirk;* (used when kernel is launched w/ TXT) */ static int force_on =0 ; -static int intel_iommu_tboot_noforce; +int intel_iommu_tboot_noforce;static int no_platform_optin;#define ROOT_ENTRY_NR (VTD_PAGE_SIZE/sizeof(struct root_entry))
drivers/iommu/intel/iommu.h +2 -0
@@ -1352,6 +1352,7 @@ static inline bool ecmd_has_pmu_essentiaextern int dmar_disabled;extern int intel_iommu_enabled; +extern int intel_iommu_tboot_noforce;#else static inline int iommu_calculate_agaw (struct intel_iommu *iommu) {
@@ -1364,6 +1365,7 @@ static inline int iommu_calculate_max_sa#define dmar_disabled (1)#define intel_iommu_enabled (0)#define intel_iommu_sm (0) +#define intel_iommu_tboot_noforce (0)#endif static inline const char *decode_prq_descriptor(char *str, size_t size,
Greg Kroah-Hartman
maintainerPATCH 307/403 platform/x86: dell-wmi-sysman: Dont hex dump attribute security buffer
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 83c80495e45eddf64c6525fb582d8db68f256b71 upstream.
set_attribute() populates the security area of the BIOS attribute request buffer with the current admin password via populate_security_buffer(), then dumps the whole request buffer with print_hex_dump_bytes(). This can expose the plaintext admin password in the kernel log.
The same issue was fixed for the password attribute path by commit d1a196e0a6dc ("platform/x86: dell-wmi-sysman: Don't hex dump plaintext password data"). Remove the remaining dump from the BIOS attribute path.
--- drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c | 1 - 1 file changed, 1 deletion(-)
drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c +0 -1
@@ -84,7 +84,6 @@ int set_attribute(const char *a_name, coif (ret <0 ) goto out; -print_hex_dump_bytes ("set attribute data: " ,DUMP_PREFIX_NONE , buffer, buffer_size); ret =call_biosattributes_interface (wmi_priv.bios_attr_wdev, buffer, buffer_size,SETATTRIBUTE_METHOD_ID );
Greg Kroah-Hartman
maintainerPATCH 308/403 platform/x86: ISST: Validate level in perf mask ioctls
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 80e0d353c86a9a168ad6d213f494796294381538 upstream.
isst_if_get_perf_level_mask() and isst_if_get_base_freq_mask() use the user-provided level as an index into perf_levels[] via _read_pp_level_info() and _read_bf_level_info(), but neither helper validates it first.
The adjacent level-info helpers reject levels above max_level before reading the same per-level register block. Add the same bounds checks to the mask helpers, and reject disabled SST-PP levels in isst_if_get_perf_level_mask() to match isst_if_get_perf_level_info().
This prevents out-of-bounds reads from the per-level offset table on invalid ioctl input.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +9 -0
@@ -1192,6 +1192,12 @@ static int isst_if_get_perf_level_mask(vif (!power_domain_info)return -EINVAL ; +if (cpumask.level > power_domain_info->max_level) +return -EINVAL ; + +if (!(power_domain_info->pp_header.level_en_mask &BIT (cpumask.level))) +return -EINVAL ; +_read_pp_level_info ("mask" , mask, cpumask.level,SST_PP_INFO_2_OFFSET ,SST_PP_RSLVD_CORE_MASK_START ,SST_PP_RSLVD_CORE_MASK_WIDTH ,SST_MUL_FACTOR_NONE )
@@ -1273,6 +1279,9 @@ static int isst_if_get_base_freq_mask(voif (!power_domain_info)return -EINVAL ; +if (cpumask.level > power_domain_info->max_level) +return -EINVAL ; +_read_bf_level_info ("BF-cpumask" , mask, cpumask.level,SST_BF_INFO_1_OFFSET ,P1_HI_CORE_MASK_START ,P1_HI_CORE_MASK_WIDTH ,SST_MUL_FACTOR_NONE )
Greg Kroah-Hartman
maintainerPATCH 309/403 platform/x86: ISST: Validate socket ID in clos_assoc ioctl
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit a89f07db0cb95c54dac4a8406c79a04e44a73c3c upstream.
isst_if_clos_assoc() validates the user-supplied socket_id with 'socket_id > topology_max_packages()', but isst_common.sst_inst[] is allocated with topology_max_packages() entries, so the valid index range is [0, topology_max_packages()). The '>' comparison lets socket_id == topology_max_packages() pass and index one entry past the array.
In addition, isst_common.sst_inst[socket_id] is NULL for an in-range package that has no bound TPMI SST instance, and the pointer is used without a NULL check. Both the out-of-bounds entry and the NULL pointer are then dereferenced by map_partition_power_domain_id() and the following power_domain_info access.
Reject socket_id >= topology_max_packages() and a NULL sst_inst, matching the checks already performed by get_instance().
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +3 -1
@@ -724,7 +724,7 @@ static long isst_if_clos_assoc(void __usif (copy_from_user (&clos_assoc, ptr,sizeof (clos_assoc)))return -EFAULT ; -if (clos_assoc.socket_id >topology_max_packages ()) +if (clos_assoc.socket_id >=topology_max_packages ())return -EINVAL ; cpu = clos_assoc.logical_cpu;
@@ -742,6 +742,8 @@ static long isst_if_clos_assoc(void __us pkg_id = clos_assoc.socket_id; sst_inst = isst_common.sst_inst[pkg_id]; +if (!sst_inst) +return -EINVAL ; punit_id =map_partition_power_domain_id (sst_inst, punit_id, &part);if (punit_id <0 )
Greg Kroah-Hartman
maintainerPATCH 310/403 mmc: via-sdmmc: stop card-detect handling on probe failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 088eaa92fcebaa6b957ccf9635afdf39643a577d upstream.
request_irq() registers the SD card-detect interrupt and the probe enables it before mmc_add_host() runs. If mmc_add_host() fails, the error path only unmaps the registers and returns: the interrupt stays registered, so the handler keeps running against the host once it is freed. via_sdc_isr() dereferences sdhost and its MMIO base and schedules carddet_work, which via_sdc_card_detect() also runs against freed memory through its container_of() dereference.
Add a probe-error path that disables and frees the interrupt and cancels carddet_work before unmapping. carddet_work can re-enable the device interrupt via via_reset_pcictrl(), which restores PCIINTCTRL, so mask it again after cancelling the work.
This issue was found by an in-house static analysis tool and confirmed by manual code review.
--- drivers/mmc/host/via-sdmmc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/mmc/host/via-sdmmc.c +7 -1
@@ -1154,10 +1154,16 @@ static int via_sd_probe(struct pci_dev * ret =mmc_add_host (mmc);if (ret) - goto unmap; + goto free_irq;return 0 ; +free_irq: +writeb (0x0 , sdhost->pcictrl_mmiobase +VIA_CRDR_PCIINTCTRL ); +free_irq (pcidev->irq, sdhost); +cancel_work_sync (&sdhost->carddet_work); +/* carddet_work may re-enable the interrupt via via_reset_pcictrl(). */ +writeb (0x0 , sdhost->pcictrl_mmiobase +VIA_CRDR_PCIINTCTRL ); unmap:iounmap (sdhost->mmiobase); free_mmc_host:
Greg Kroah-Hartman
maintainerPATCH 311/403 platform/x86: ISST: Add a NULL check for sst_inst[]
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit 3de2776e9d7073765c10c2326c2bda5926811ea6 upstream.
To be consistent with other places, add a NULL check for failed socket loading by checking isst_common.sst_inst[].
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +2 -0
@@ -1312,6 +1312,8 @@ static int isst_if_get_tpmi_instance_coureturn -EINVAL ; sst_inst = isst_common.sst_inst[tpmi_inst.socket_id]; +if (!sst_inst) +return -EINVAL ; tpmi_inst.count =isst_instance_count (sst_inst);
Greg Kroah-Hartman
maintainerPATCH 312/403 platform/x86: ISST: Just allow 2 bits for SST feature enable
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit 0f377f2b47646abe6ec3616ae6a8670d9ff7eb86 upstream.
Currently only 2 features SST-TF and SST-BF are supported, so only allow bit 0 and bit 1.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +4 -0
@@ -841,6 +841,7 @@ static long isst_if_clos_assoc(void __us#define SST_PP_FEATURE_STATE_START 8#define SST_PP_FEATURE_STATE_WIDTH 8 +#define SST_PP_FEATURE_STATE_VALID_MASK GENMASK(1, 0)#define SST_BF_FEATURE_SUPPORTED_START 12#define SST_BF_FEATURE_SUPPORTED_WIDTH 1
@@ -991,6 +992,9 @@ static int isst_if_set_perf_feature(voidif (power_domain_info->write_blocked)return -EPERM ; +if (perf_feature.feature & ~SST_PP_FEATURE_STATE_VALID_MASK ) +return -EINVAL ; +_write_pp_info ("perf_feature" , perf_feature.feature,SST_PP_CONTROL_OFFSET ,SST_PP_FEATURE_STATE_START ,SST_PP_FEATURE_STATE_WIDTH ,SST_MUL_FACTOR_NONE )
Greg Kroah-Hartman
maintainerPATCH 313/403 platform/x86: ISST: Use PP level enable mask
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit 9b9026943b19d06ebf520b1f4786621947cf43c8 upstream.
Add check for enabled levels only when reading MMIO. Some levels can be disabled by BIOS. If the level is not enabled, return an error.
Reset the enable and allowed level masks if there is a failure to add a perf level.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 16 ++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +14 -2
@@ -334,8 +334,11 @@ static int sst_add_perf_profiles(structint i; pd_info->perf_levels =devm_kcalloc (dev, levels,sizeof (struct perf_level ),GFP_KERNEL ); -if (!pd_info->perf_levels) +if (!pd_info->perf_levels) { + pd_info->pp_header.allowed_level_mask =0 ; + pd_info->pp_header.level_en_mask =0 ;return 0 ; + } pd_info->ratio_unit = pd_info->pp_header.ratio_unit; pd_info->avx_levels =SST_MAX_AVX_LEVELS ;
@@ -874,7 +877,7 @@ static int isst_if_get_perf_level(void _ SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, SST_MUL_FACTOR_NONE) perf_level.enabled = !!(power_domain_info->sst_header.cap_mask &BIT (1 )); - level_mask = perf_level.level_mask; + level_mask = perf_level.level_mask & power_domain_info->pp_header.level_en_mask; perf_level.sst_bf_support =0 ;for_each_set_bit (level, &level_mask,BITS_PER_BYTE ) {/*
@@ -1249,6 +1252,9 @@ static int isst_if_get_base_freq_info(voif (base_freq.level > power_domain_info->max_level)return -EINVAL ; +if (!(power_domain_info->pp_header.level_en_mask &BIT (base_freq.level))) +return -EINVAL ; +_read_bf_level_info ("p1_high" , base_freq.high_base_freq_mhz, base_freq.level,SST_BF_INFO_0_OFFSET ,SST_BF_P1_HIGH_START ,SST_BF_P1_HIGH_WIDTH ,SST_MUL_FACTOR_FREQ )
@@ -1288,6 +1294,9 @@ static int isst_if_get_base_freq_mask(voif (cpumask.level > power_domain_info->max_level)return -EINVAL ; +if (!(power_domain_info->pp_header.level_en_mask &BIT (cpumask.level))) +return -EINVAL ; +_read_bf_level_info ("BF-cpumask" , mask, cpumask.level,SST_BF_INFO_1_OFFSET ,P1_HI_CORE_MASK_START ,P1_HI_CORE_MASK_WIDTH ,SST_MUL_FACTOR_NONE )
@@ -1376,6 +1385,9 @@ static int isst_if_get_turbo_freq_info(vif (turbo_freq.level > power_domain_info->max_level)return -EINVAL ; +if (!(power_domain_info->pp_header.level_en_mask &BIT (turbo_freq.level))) +return -EINVAL ; + turbo_freq.max_buckets =TRL_MAX_BUCKETS ; turbo_freq.max_trl_levels =TRL_MAX_LEVELS ; turbo_freq.max_clip_freqs =SST_TF_MAX_LP_CLIP_RATIOS ;
Greg Kroah-Hartman
maintainerPATCH 314/403 platform/x86: ISST: Validate logical CPU id and clos id
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit 124e2dbabe460c2a6e7440f4ad8af560131295c9 upstream.
Validate max CLOS ID and logical CPU ID for core power feature. Reject any clos level or logical CPU number greater than the supported maximum. These are used to calculate MMIO offset.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 13 ++++++++++++ 1 file changed, 13 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +13 -0
@@ -647,6 +647,8 @@ static long isst_if_core_power_state(voi#define SST_CLOS_CONFIG_MAX_START 16#define SST_CLOS_CONFIG_MAX_WIDTH 8 +#define SST_MAX_CLOS 3 +static long isst_if_clos_param (void __user *argp) {struct tpmi_per_power_domain_info *power_domain_info;
@@ -655,6 +657,9 @@ static long isst_if_clos_param(void __usif (copy_from_user (&clos_param, argp,sizeof (clos_param)))return -EFAULT ; +if (clos_param.clos >SST_MAX_CLOS ) +return -EINVAL ; + power_domain_info =get_instance (clos_param.socket_id, clos_param.power_domain_id);if (!power_domain_info)return -EINVAL ;
@@ -701,6 +706,8 @@ static long isst_if_clos_param(void __us#define SST_CLOS_ASSOC_CPUS_PER_REG 16#define SST_CLOS_ASSOC_BITS_PER_CPU 4 +#define SST_CLOS_ASSOC_MAX_LOGICAL_CPU 63 +static long isst_if_clos_assoc (void __user *argp) {struct isst_if_clos_assoc_cmds assoc_cmds;
@@ -727,9 +734,15 @@ static long isst_if_clos_assoc(void __usif (copy_from_user (&clos_assoc, ptr,sizeof (clos_assoc)))return -EFAULT ; +if (clos_assoc.clos >SST_MAX_CLOS ) +return -EINVAL ; +if (clos_assoc.socket_id >=topology_max_packages ())return -EINVAL ; +if (clos_assoc.logical_cpu >SST_CLOS_ASSOC_MAX_LOGICAL_CPU ) +return -EINVAL ; + cpu = clos_assoc.logical_cpu; clos = clos_assoc.clos;
Greg Kroah-Hartman
maintainerPATCH 315/403 platform/x86: ISST: Validate parameter for core power state
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit 1700b4f804555467b7eff58dff7acc11d508b3a1 upstream.
Allow only 0 or 1 for core_power enable and priority_type parameters.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 7 +++++++ 1 file changed, 7 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +7 -0
@@ -597,6 +597,9 @@ static bool disable_dynamic_sst_features#define SST_CP_PRIORITY_TYPE_START 1#define SST_CP_PRIORITY_TYPE_WIDTH 1 +#define SST_CP_MAX_ENABLE 1 +#define SST_CP_MAX_PRIORITY_TYPE 1 +static long isst_if_core_power_state (void __user *argp) {struct tpmi_per_power_domain_info *power_domain_info;
@@ -616,6 +619,10 @@ static long isst_if_core_power_state(voiif (power_domain_info->write_blocked)return -EPERM ; +if (core_power.enable >SST_CP_MAX_ENABLE || + core_power.priority_type >SST_CP_MAX_PRIORITY_TYPE ) +return -EINVAL ; +_write_cp_info ("cp_enable" , core_power.enable,SST_CP_CONTROL_OFFSET , SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE) _write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET,
Greg Kroah-Hartman
maintainerPATCH 316/403 platform/x86: ISST: Validate parameter for frequency and priority
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit 574b59bb4b6bfcfd1f639d02f1041b314d43a2e6 upstream.
Validate range for frequency and proportional priority while setting CLOS parameters.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +12 -0
@@ -656,6 +656,9 @@ static long isst_if_core_power_state(voi#define SST_MAX_CLOS 3 +#define SST_MAX_FREQ 0xff +#define SST_CLOS_MAX_PRIORITY 0x0f +static long isst_if_clos_param (void __user *argp) {struct tpmi_per_power_domain_info *power_domain_info;
@@ -675,6 +678,15 @@ static long isst_if_clos_param(void __usif (power_domain_info->write_blocked)return -EPERM ; +if (!in_range (clos_param.min_freq_mhz /SST_MUL_FACTOR_FREQ ,0 ,SST_MAX_FREQ +1 )) +return -EINVAL ; + +if (!in_range (clos_param.max_freq_mhz /SST_MUL_FACTOR_FREQ ,0 ,SST_MAX_FREQ +1 )) +return -EINVAL ; + +if (!in_range (clos_param.prop_prio,0 ,SST_CLOS_MAX_PRIORITY +1 )) +return -EINVAL ; + _write_cp_info("clos.min_freq", clos_param.min_freq_mhz, (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH,
Greg Kroah-Hartman
maintainerPATCH 317/403 platform/x86: ISST: Return error during profile addition
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit f9a647cb8d90c09633a49a1e766e140e78012444 upstream.
If sst_add_perf_profiles() fails for memory allocation, it continues to allow SST-CP (core-power) feature. But in practice this is not very useful as to achieve some frequencies via SST-CP, an SST-PP (perf-profile) level change is required.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +7 -3
@@ -337,7 +337,7 @@ static int sst_add_perf_profiles(structif (!pd_info->perf_levels) { pd_info->pp_header.allowed_level_mask =0 ; pd_info->pp_header.level_en_mask =0 ; -return 0 ; +return -ENOMEM ; } pd_info->ratio_unit = pd_info->pp_header.ratio_unit;
@@ -368,7 +368,7 @@ static int sst_add_perf_profiles(structstatic int sst_main (struct auxiliary_device *auxdev,struct tpmi_per_power_domain_info *pd_info) {struct device *dev = &auxdev->dev; -int i, mask, levels; +int i, ret, mask, levels; *((u64 *)&pd_info->sst_header) =readq (pd_info->sst_base); pd_info->sst_header.cp_offset *=8 ;
@@ -400,8 +400,12 @@ static int sst_main(struct auxiliary_dev levels = i; mask <<=1 ; } + + ret =sst_add_perf_profiles (auxdev, pd_info, levels +1 ); +if (ret) +return ret; + pd_info->max_level = levels; -sst_add_perf_profiles (auxdev, pd_info, levels +1 );return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 318/403 platform/x86: ishtp_eclite: Fix ACPI device reference leak in probe error path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ma Ke <[email protected]>
commit 62b57396c26a1ce54963709928ea0d01fa522eea upstream.
ecl_ishtp_cl_probe() acquires a reference to an ACPI device via acpi_find_eclite_device() but fails to release it in the error path when acpi_opregion_init() fails. This results in a reference count leak, preventing proper cleanup of the ACPI device.
Calling path: acpi_find_eclite_device() -> acpi_dev_get_first_match_dev() -> acpi_dev_get_next_match_dev() -> bus_find_device() -> get_device().
Found by code review.
--- drivers/platform/x86/intel/ishtp_eclite.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/platform/x86/intel/ishtp_eclite.c +4 -1
@@ -600,13 +600,16 @@ static int ecl_ishtp_cl_probe(struct ish rv =acpi_opregion_init (opr_dev);if (rv) {dev_err (cl_data_to_dev (opr_dev),"ACPI opregion init failed\n" ); - goto err_exit; + goto err_put; }/* Reprobe devices depending on ECLite - battery, fan, etc. */ acpi_dev_clear_dependencies (opr_dev->adev);return 0 ; + +err_put: +acpi_dev_put (opr_dev->adev); err_exit:ishtp_set_connection_state (ecl_ishtp_cl,ISHTP_CL_DISCONNECTING );ishtp_cl_disconnect (ecl_ishtp_cl);
Greg Kroah-Hartman
maintainerPATCH 319/403 platform/chrome: sensorhub: Bound the EC-reported sensor number
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 833740a2333c2e4db4e02e3d0ffba04e8718a5f3 upstream.
Each EC FIFO event carries an 8-bit sensor number (in->sensor_num). cros_ec_sensorhub_ring_handler() validates the FIFO event count, the per-read count and the ring bound, but not the sensor number, which cros_ec_sensor_ring_process_event() then uses unchecked to index sensorhub->batch_state[] - allocated with only sensorhub->sensor_num entries. A sensor number of sensor_num or larger is an out-of-bounds read and write of batch_state[].
Validate the sensor number in the ring handler, where each event is read from the EC, and drop a malformed event before it is used.
--- drivers/platform/chrome/cros_ec_sensorhub_ring.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/platform/chrome/cros_ec_sensorhub_ring.c +8 -0
@@ -879,6 +879,14 @@ static void cros_ec_sensorhub_ring_handlfor (in = sensorhub->resp->fifo_read.data, j =0 ; j < number_data; j++, in++) { +/* Skip event if sensor_num from EC is out of bounds. */ +if (in->sensor_num >= sensorhub->sensor_num) { +dev_warn_ratelimited (sensorhub->dev, +"Invalid sensor number %u from EC\n" , + in->sensor_num); +continue ; + } + if (cros_ec_sensor_ring_process_event( sensorhub, fifo_info, fifo_timestamp,
Greg Kroah-Hartman
maintainerPATCH 320/403 platform/x86: hp-bioscfg: accept reduced ACPI packages from older HP BIOS
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 40e10e6cc8f70c041431a1e30186807e28ec46e0 upstream.
hp_init_bios_package_attribute() hard-fails when a WMI ACPI package contains fewer elements than the type-specific expected count (e.g. 11 elements instead of 13 for INTEGER or ENUMERATION attributes). This causes the entire hp_bioscfg driver to skip attribute enumeration on older HP hardware whose BIOS returns shortened packages when optional fields like prerequisites or possible values are absent.
Observed on HP EliteBook 840 G2 (BIOS M71 Ver. 01.31):
hp_bioscfg: ACPI-package does not have enough elements: 11 < 13
The element layout has two tiers:
- Elements 0-9 (SECURITY_LEVEL+1 = 10): common to all attribute types
- Elements 10-N: type-specific (bounds, values, encodings, ...)
The per-type populate functions (hp_populate_*_elements_from_package) already handle sparse packages correctly via their own elem < count loop guards and inner-loop bounds checks. The only unsafe case is when we lack even the common elements needed to register the attribute.
Fix by introducing COMMON_ELEM_CNT to mark the hard minimum (10), and splitting the check into two tiers:
- Fewer than COMMON_ELEM_CNT elements: hard fail, can't proceed.
- Fewer than expected type-specific elements: warn, but let the populate function parse what is available.
--- drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 11 ++++++++--- drivers/platform/x86/hp/hp-bioscfg/bioscfg.h | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +8 -3
@@ -664,12 +664,17 @@ static int hp_init_bios_package_attributint ret =0 ;/* Take action appropriate to each ACPI TYPE */ -if (obj->package.count < min_elements) { -pr_err ("ACPI-package does not have enough elements: %d < %d\n" , - obj->package.count, min_elements); +if (obj->package.count <COMMON_ELEM_CNT ) { +pr_err ("ACPI-package is missing common elements: %d < %d\n" , + obj->package.count,COMMON_ELEM_CNT ); goto pack_attr_exit; } +if (obj->package.count < min_elements) { +pr_warn ("ACPI-package has fewer elements than expected: %d < %d, parsing available elements\n" , + obj->package.count, min_elements); + } + elements = obj->package.elements;/* sanity checking */
drivers/platform/x86/hp/hp-bioscfg/bioscfg.h +3 -0
@@ -279,6 +279,9 @@ enum hp_wmi_data_elements {PSWD_ENCODINGS =13 ,PSWD_IS_SET =14 ,PSWD_ELEM_CNT =15 , + +/* Minimum elements shared by all attribute types (NAME..SECURITY_LEVEL) */ + COMMON_ELEM_CNT = SECURITY_LEVEL + 1, };#define GET_INSTANCE_ID (type) \
Greg Kroah-Hartman
maintainerPATCH 321/403 platform/x86: hp-bioscfg: advance elem past consumed array elements
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 05c808362e808e196f75696b8a64f7aa8b2245ce upstream.
The outer parsing loop in each attribute-type parser advances "elem" (the index into the ACPI package element array) by exactly one per iteration, but cases that consume multi-element arrays (PREREQUISITES, ENUM_POSSIBLE_VALUES, PSWD_ENCODINGS) read "size" consecutive elements without adjusting "elem" for the extra entries consumed beyond the first. The next outer iteration then re-reads a leftover element from the array just consumed instead of the next real property, and the type check fails on that stale element, aborting the parse with -EIO.
This produces exactly the failure visible in dmesg on the test hardware, on every boot:
Error expected type 2 for elem 13, but got type 1 instead
hp_bioscfg: Returned error 0x3, "Invalid command value/Feature not supported"
Fix by advancing "elem" by (size - 1) after each array-consuming loop, so the outer loop's own "elem++" lands on the correct next element. "eloc" is intentionally left alone: it indexes the logical property schema, not the physical element array, and each array case is still exactly one logical property regardless of how many physical elements it spans.
The defect is identical across all five attribute-type parsers (enum, integer, string, ordered-list, password), which were copy-pasted from the same template when the driver was introduced.
--- drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c | 4 ++++ drivers/platform/x86/hp/hp-bioscfg/int-attributes.c | 2 ++ drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 2 ++ drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 4 ++++ drivers/platform/x86/hp/hp-bioscfg/string-attributes.c | 2 ++ 5 files changed, 14 insertions(+)
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +4 -0
@@ -227,6 +227,8 @@ static int hp_populate_enumeration_elemekfree (str_value); str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL :
@@ -280,6 +282,8 @@ static int hp_populate_enumeration_elemekfree (str_value); str_value =NULL ; } +if (size) + elem += (size <MAX_VALUES_SIZE ? size :MAX_VALUES_SIZE ) -1 ;break ;default :pr_warn ("Invalid element: %d found in Enumeration attribute or data may be malformed\n" , elem);
drivers/platform/x86/hp/hp-bioscfg/int-attributes.c +2 -0
@@ -243,6 +243,8 @@ static int hp_populate_integer_elements_kfree (str_value); str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL :
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +2 -0
@@ -233,6 +233,8 @@ static int hp_populate_ordered_list_elemkfree (str_value); str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL :
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +4 -0
@@ -321,6 +321,8 @@ static int hp_populate_password_elements str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL : password_data->common.security_level = int_value;
@@ -362,6 +364,8 @@ static int hp_populate_password_elements str_value =NULL ; } +if (size) + elem += size -1 ;break ;case PSWD_IS_SET : password_data->is_enabled = int_value;
drivers/platform/x86/hp/hp-bioscfg/string-attributes.c +2 -0
@@ -233,6 +233,8 @@ static int hp_populate_string_elements_fkfree (str_value); str_value =NULL ; } +if (size) + elem += size -1 ;break ;case SECURITY_LEVEL :
Greg Kroah-Hartman
maintainerPATCH 322/403 platform/x86: hp-bioscfg: bound ordered-list parsing by the package count
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 1d143d78299d0eb4536698bf98c1815ec69f22a9 upstream.
hp_populate_ordered_list_elements_from_package() differs from the other per-type parsers: its main loop is bounded only by the fixed per-type count and never checks elem against the number of elements actually present in the package,
for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT; elem++, eloc++)
whereas the string, integer, enumeration and password parsers bound their main loop with "elem < count" as well.
This is safe today because hp_init_bios_package_attribute() rejects any package with fewer than ORD_ELEM_CNT elements before the parser runs. An upcoming change, however, relaxes that check to accept shorter packages.
Bound the loop by the validated element count as well, so it stops at whichever comes first, the per-type count or the real package size,
for (elem = 1, eloc = 1; eloc < ORD_ELEM_CNT && elem < order_obj_count;
elem++, eloc++)order_obj_count is the validated element count, now correctly forwarded from the caller. No functional change for packages that enumerate correctly today.
--- drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +1 -1
@@ -146,7 +146,7 @@ static int hp_populate_ordered_list_elemif (!order_obj)return -EINVAL ; -for (elem =1 , eloc =1 ; eloc <ORD_ELEM_CNT ; elem++, eloc++) { +for (elem =1 , eloc =1 ; eloc <ORD_ELEM_CNT && elem < order_obj_count; elem++, eloc++) {switch (order_obj[elem].type) {case ACPI_TYPE_STRING :
Greg Kroah-Hartman
maintainerPATCH 323/403 platform/x86: hp-bioscfg: fix heap OOB read in sk_store() and kek_store()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit a7508c7959ff8d037327d377ed21a9c0eabe4674 upstream.
sk_store() and kek_store() strip a trailing newline from the sysfs write before allocating the key buffer:
length = count; if (buf[length - 1] == '\n') length--; bioscfg_drv.spm_data.signing_key = kmemdup(buf, length, GFP_KERNEL);
but then pass the original "count" (not "length") as the copy size to hp_wmi_perform_query(), which memcpy()s that many bytes out of the "length"-sized allocation, reading one byte past it whenever the write ends in a newline, the normal case for a shell "echo" into sysfs.
KASAN confirms this directly:
BUG: KASAN: slab-out-of-bounds in hp_wmi_perform_query+0x1e9/0x460 [hp_bioscfg] Read of size 28 at addr ffff88813c8e2b80 by task python3/16022 ... sk_store+0xa7/0x240 [hp_bioscfg] kernfs_fop_write_iter+0x3e1/0x5d0 ... The buggy address is located 0 bytes inside of allocated 27-byte region [ffff88813c8e2b80, ffff88813c8e2b9b)
Reproduced identically for kek_store, and at multiple write sizes (28, 57, 201 bytes), each time reading exactly one byte past a kmemdup() allocation one byte smaller than the write.
Fix by passing "length" instead of "count" to hp_wmi_perform_query() in both functions.
--- drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c +2 -2
@@ -238,7 +238,7 @@ static ssize_t sk_store(struct kobject * ret =hp_wmi_perform_query (HPWMI_SECUREPLATFORM_SET_SK ,HPWMI_SECUREPLATFORM , (void *)bioscfg_drv.spm_data.signing_key, - count,0 ); + length,0 );if (!ret) { bioscfg_drv.spm_data.mechanism =SIGNING_KEY ;
@@ -274,7 +274,7 @@ static ssize_t kek_store(struct kobject ret =hp_wmi_perform_query (HPWMI_SECUREPLATFORM_SET_KEK ,HPWMI_SECUREPLATFORM , (void *)bioscfg_drv.spm_data.endorsement_key, - count,0 ); + length,0 );if (!ret) { bioscfg_drv.spm_data.mechanism =ENDORSEMENT_KEY ;
Greg Kroah-Hartman
maintainerPATCH 324/403 platform/x86: hp-bioscfg: fix heap OOB read on empty password write
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 2b2ec354f905c14e3270e8ec3ab50f7d8ad73bab upstream.
validate_password_input() computes length = strlen(buf) and then checks buf[length - 1] to strip a trailing newline, without checking that length is nonzero first. Writing an empty string (a bare '\n') to current_password or new_password gives length == 0, and buf[length - 1] reads buf[-1], one byte before the heap allocation holding the copied input.
KASAN confirms this directly:
BUG: KASAN: slab-out-of-bounds in store_password_instance.constprop.0+0x223/0x2a0 [hp_bioscfg] Read of size 1 at addr ffff88811bd8da9f by task sh/13740 ... store_password_instance.constprop.0+0x223/0x2a0 [hp_bioscfg] current_password_store+0x14/0x20 [hp_bioscfg] ... The buggy address is located 23 bytes to the right of allocated 8-byte region [ffff88811bd8da80, ffff88811bd8da88)
Reproduced identically via new_password_store. Execution continues past the bad read (the garbage byte only affects whether "length" is decremented by one), so the write completes and returns success; this is a pure information read past the buffer, not a crash, but it is still an out-of-bounds access KASAN correctly flags.
Fix by only checking buf[length - 1] when length is nonzero.
--- drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +1 -1
@@ -66,7 +66,7 @@ static int validate_password_input(int istruct password_data *password_data = &bioscfg_drv.password_data[instance_id]; length =strlen (buf); -if (buf[length -1 ] =='\n' ) +if (length >0 && buf[length -1 ] =='\n' ) length--;if (length >MAX_PASSWD_SIZE )
Greg Kroah-Hartman
maintainerPATCH 325/403 platform/x86: hp-bioscfg: fix new_password_store() overwriting current_password
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit 2ea12a467a9cb12170417b30784fe26a243a75fe upstream.
current_password_store() and new_password_store() both call store_password_instance() with is_current = true:
static ssize_t new_password_store(...)
{
return store_password_instance(kobj, buf, count, true);
}so a write to new_password is routed to current_password instead, and the new_password field is never written by either sysfs entry point.
Fix by passing false from new_password_store(), matching what the is_current parameter is meant to select.
--- drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +1 -1
@@ -123,7 +123,7 @@ static ssize_t new_password_store(structstruct kobj_attribute *attr, const char *buf, size_t count) { - return store_password_instance(kobj, buf, count, true); + return store_password_instance(kobj, buf, count, false); }static struct kobj_attribute password_new_password =__ATTR_WO (new_password);
Greg Kroah-Hartman
maintainerPATCH 326/403 platform/x86: hp-bioscfg: fix off-by-one write in hp_get_string_from_buffer()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit dc03f05e419f3460342fb7564884f244622634b6 upstream.
hp_get_string_from_buffer() clamps the converted string length against the destination buffer size with "size > dst_size", so when the converted length is exactly equal to dst_size, conv_dst_size is left at dst_size and the unconditional NUL terminator write
dst[conv_dst_size] = 0;
lands one byte past the destination buffer. This is the same shape of bug as the previously fixed off-by-one in hp_convert_hexstr_to_str():
the buffer is sized correctly for the content, but the terminator write is never checked against that size.
Fix by changing the comparison to ">=" so conv_dst_size is always left with room for the terminator.
All fixed-size destinations that reach this function (path[512], current_value[512], current_password/current_value[64], and the per-entry buffers in encodings[][512] and prerequisites[][512]) are affected.
--- drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +1 -1
@@ -85,7 +85,7 @@ int hp_get_string_from_buffer(u8 **buffe* bytes. */ conv_dst_size = size; -if (size > dst_size) +if (size >= dst_size) conv_dst_size = dst_size -1 ;/*
Greg Kroah-Hartman
maintainerPATCH 327/403 platform/x86: hp-bioscfg: fix ORD_LIST_ELEMENTS never being parsed
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit cb6b1b0fb236a9581cae213c2a9182e68cc3ffe5 upstream.
The ACPI_TYPE_STRING case explicitly skips the string conversion for elem == ORD_LIST_ELEMENTS:
if (elem != PREREQUISITES && elem != ORD_LIST_ELEMENTS) {
ret = hp_convert_hexstr_to_str(..., &str_value, &value_len);
if (ret)
continue;
}so by the time the ORD_LIST_ELEMENTS case in the eloc switch runs, str_value is NULL (it was freed and reset to NULL at the end of the previous iteration). That case then does:
ret = hp_convert_hexstr_to_str(str_value, value_len, &tmpstr, &tmp_len);
hp_convert_hexstr_to_str() rejects a NULL input with -EINVAL, which sends this function to exit_list, and exit_list unconditionally returns 0. The net effect is that any ordered-list attribute with elements present silently ends up with an empty elements list, with no error surfaced anywhere.
Fix by converting the current element directly, order_obj[elem], the same way the PREREQUISITES case already handles its own array elements, instead of reusing the unrelated str_value/value_len left over from earlier processing.
--- drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +3 -1
@@ -264,7 +264,9 @@ static int hp_populate_ordered_list_elem* Ordered list data is stored in hex and comma separated format * Convert the data and split it to show each element */ - ret =hp_convert_hexstr_to_str (str_value, value_len, &tmpstr, &tmp_len); + ret =hp_convert_hexstr_to_str (order_obj[elem].string.pointer, + order_obj[elem].string.length, + &tmpstr, &tmp_len);if (ret) goto exit_list;
Greg Kroah-Hartman
maintainerPATCH 328/403 platform/x86: hp-bioscfg: pass validated element count to package parsers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit e0ddfd77c0c320b7d12b6c9169303b140b798775 upstream.
The per-type package parsers are handed the wrong element count.
hp_init_bios_package_attribute() validates obj->package.count and then calls one of the five hp_populate_*_package_data() wrappers (string, integer, enumeration, ordered list, password). Each wrapper forwards a count to its hp_populate_*_elements_from_package() parser, but instead of forwarding the validated obj->package.count it derives the count from elements[0]. elements[0] is the NAME field and is always an ACPI_TYPE_STRING, so reading ->package.count from it in fact reads ->string.length through the union acpi_object. The parsers thus bound themselves against the length of the name string rather than against the real number of elements in the package.
This is safe today because hp_init_bios_package_attribute() refuses any package that has fewer than the type's element count, so a parser only ever runs on a full package and never reads past it regardless of the bogus bound.
An upcoming change relaxes that check to accept shorter packages. Once a parser can receive fewer elements than its per-type count, a bound taken from the name length no longer reflects the array size, and the "elem < count" loop conditions and "elem + n >= count" sub-loop guards read past the end of elements[] - an out-of-bounds heap read.
Forward the validated obj->package.count to every *_package_data() wrapper so the parsers bound themselves against the real package size. This does not change behaviour for the packages that enumerate correctly today and is a prerequisite for accepting shorter packages safely.
--- drivers/platform/x86/hp/hp-bioscfg/bioscfg.c | 5 +++++ drivers/platform/x86/hp/hp-bioscfg/bioscfg.h | 5 +++++ drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c | 4 +++- drivers/platform/x86/hp/hp-bioscfg/int-attributes.c | 4 +++- drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c | 6 ++++-- drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 6 ++++-- drivers/platform/x86/hp/hp-bioscfg/string-attributes.c | 4 +++- 7 files changed, 27 insertions(+), 7 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +5 -0
@@ -739,26 +739,31 @@ static int hp_init_bios_package_attributswitch (attr_type) {case HPWMI_STRING_TYPE : ret =hp_populate_string_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;case HPWMI_INTEGER_TYPE : ret =hp_populate_integer_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;case HPWMI_ENUMERATION_TYPE : ret =hp_populate_enumeration_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;case HPWMI_ORDERED_LIST_TYPE : ret =hp_populate_ordered_list_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;case HPWMI_PASSWORD_TYPE : ret =hp_populate_password_package_data (elements, + obj->package.count, instance_id, attr_name_kobj);break ;
drivers/platform/x86/hp/hp-bioscfg/bioscfg.h +5 -0
@@ -404,6 +404,7 @@ int hp_populate_string_buffer_data(u8 *bint hp_alloc_string_data (void );void hp_exit_string_attributes (void );int hp_populate_string_package_data (union acpi_object *str_obj, +int str_obj_count,int instance_id,struct kobject *attr_name_kobj);
@@ -414,6 +415,7 @@ int hp_populate_integer_buffer_data(u8 *int hp_alloc_integer_data (void );void hp_exit_integer_attributes (void );int hp_populate_integer_package_data (union acpi_object *integer_obj, +int integer_obj_count,int instance_id,struct kobject *attr_name_kobj);
@@ -424,6 +426,7 @@ int hp_populate_enumeration_buffer_data(int hp_alloc_enumeration_data (void );void hp_exit_enumeration_attributes (void );int hp_populate_enumeration_package_data (union acpi_object *enum_obj, +int enum_obj_count,int instance_id,struct kobject *attr_name_kobj);
@@ -435,6 +438,7 @@ int hp_populate_ordered_list_buffer_dataint hp_alloc_ordered_list_data (void );void hp_exit_ordered_list_attributes (void );int hp_populate_ordered_list_package_data (union acpi_object *order_obj, +int order_obj_count,int instance_id,struct kobject *attr_name_kobj);
@@ -443,6 +447,7 @@ int hp_populate_password_buffer_data(u8int instance_id, struct kobject *attr_name_kobj);int hp_populate_password_package_data (union acpi_object *password_obj, +int password_obj_count,int instance_id,struct kobject *attr_name_kobj);int hp_alloc_password_data (void );
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +3 -1
@@ -304,10 +304,12 @@ exit_enumeration_package:* Populate all properties of an instance under enumeration attribute * * @enum_obj: ACPI object with enumeration data +* @enum_obj_count: Number of elements in @enum_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_enumeration_package_data (union acpi_object *enum_obj, +int enum_obj_count,int instance_id,struct kobject *attr_name_kobj) {
@@ -316,7 +318,7 @@ int hp_populate_enumeration_package_data enum_data->attr_name_kobj = attr_name_kobj;hp_populate_enumeration_elements_from_package (enum_obj, - enum_obj->package.count, + enum_obj_count, instance_id);hp_update_attribute_permissions (enum_data->common.is_readonly, &enumeration_current_val);
drivers/platform/x86/hp/hp-bioscfg/int-attributes.c +3 -1
@@ -277,10 +277,12 @@ exit_integer_package:* Populate all properties of an instance under integer attribute * * @integer_obj: ACPI object with integer data +* @integer_obj_count: Number of elements in @integer_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_integer_package_data (union acpi_object *integer_obj, +int integer_obj_count,int instance_id,struct kobject *attr_name_kobj) {
@@ -288,7 +290,7 @@ int hp_populate_integer_package_data(uni integer_data->attr_name_kobj = attr_name_kobj;hp_populate_integer_elements_from_package (integer_obj, - integer_obj->package.count, + integer_obj_count, instance_id);hp_update_attribute_permissions (integer_data->common.is_readonly, &integer_current_val);
drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +4 -2
@@ -303,10 +303,12 @@ exit_list:* Populate all properties of an instance under ordered_list attribute * * @order_obj: ACPI object with ordered_list data +* @order_obj_count: Number of elements in @order_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ -int hp_populate_ordered_list_package_data (union acpi_object *order_obj,int instance_id, +int hp_populate_ordered_list_package_data (union acpi_object *order_obj,int order_obj_count, +int instance_id,struct kobject *attr_name_kobj) {struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance_id];
@@ -314,7 +316,7 @@ int hp_populate_ordered_list_package_dat ordered_list_data->attr_name_kobj = attr_name_kobj;hp_populate_ordered_list_elements_from_package (order_obj, - order_obj->package.count, + order_obj_count, instance_id);hp_update_attribute_permissions (ordered_list_data->common.is_readonly, &ordered_list_current_val);
drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +4 -2
@@ -389,10 +389,12 @@ exit_package:* Populate all properties for an instance under password attribute * * @password_obj: ACPI object with password data +* @password_obj_count: Number of elements in @password_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ -int hp_populate_password_package_data (union acpi_object *password_obj,int instance_id, +int hp_populate_password_package_data (union acpi_object *password_obj,int password_obj_count, +int instance_id,struct kobject *attr_name_kobj) {struct password_data *password_data = &bioscfg_drv.password_data[instance_id];
@@ -400,7 +402,7 @@ int hp_populate_password_package_data(un password_data->attr_name_kobj = attr_name_kobj;hp_populate_password_elements_from_package (password_obj, - password_obj->package.count, + password_obj_count, instance_id); hp_friendly_user_name_update(password_data->common.path,
drivers/platform/x86/hp/hp-bioscfg/string-attributes.c +3 -1
@@ -265,10 +265,12 @@ exit_string_package:* Populate all properties of an instance under string attribute * * @string_obj: ACPI object with string data +* @string_obj_count: Number of elements in @string_obj * @instance_id: The instance to enumerate * @attr_name_kobj: The parent kernel object */ int hp_populate_string_package_data (union acpi_object *string_obj, +int string_obj_count,int instance_id,struct kobject *attr_name_kobj) {
@@ -277,7 +279,7 @@ int hp_populate_string_package_data(unio string_data->attr_name_kobj = attr_name_kobj;hp_populate_string_elements_from_package (string_obj, - string_obj->package.count, + string_obj_count, instance_id); hp_update_attribute_permissions(string_data->common.is_readonly,
Greg Kroah-Hartman
maintainerPATCH 329/403 platform/x86: hp-bioscfg: warn on element type mismatch instead of failing
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Muhammad Bilal <[email protected]>
commit b0e2af3ec94e0431adb59d9f249ebbd3b7285158 upstream.
hp_populate_enumeration_elements_from_package() returns -EIO and aborts enumeration of the entire attribute when any single element has an unexpected ACPI type. This is observed on HP EliteBook 840 G2 when the BIOS returns malformed ACPI data following a failed WMI query:
ACPI BIOS Error (bug): AE_AML_BUFFER_LIMIT, Index (0x000000032)
is beyond end of object (length 0x32)
ACPI Error: Aborting method \_SB.WMID.WQBE due to previous error Error expected type 2 for elem 13, but got type 1 instead
hp_bioscfg: Returned error 0x3,
"Invalid command value/Feature not supported"
Aborting immediately discards the attribute entirely.
Warn about the unexpected element type, free the temporary string, skip the offending element, and continue parsing the remaining package instead of failing the whole attribute.
--- drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +4 -3
@@ -163,10 +163,11 @@ static int hp_populate_enumeration_eleme/* Check that both expected and read object type match */ if (expected_enum_types[eloc] != enum_obj[elem].type) { -pr_err ("Error expected type %d for elem %d, but got type %d instead\n" , - expected_enum_types[eloc], elem, enum_obj[elem].type); +pr_warn ("Unexpected element type at elem %d: expected %d, got %d, skipping\n" , + elem, expected_enum_types[eloc], enum_obj[elem].type);kfree (str_value); -return -EIO ; + str_value =NULL ; +continue ; }/* Assign appropriate element value to corresponding field */
Greg Kroah-Hartman
maintainerPATCH 330/403 interconnect: Fix use after free in icc_get() and of_icc_get_by_index()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kuan-Wei Chiu <[email protected]>
commit 25c7e242aca084fdc1098248194032317dca625d upstream.
In of_icc_get_by_index() and icc_get(), if the dynamic allocation for path->name fails via kasprintf(), the error handling path directly calls kfree(path) to free the path object and returns an error.
However, prior to this point, path_find() calls path_init(), which already links the path's requests into the req_list of the respective interconnect nodes via hlist_add_head(). Directly invoking kfree(path) leaves dangling pointers in the hlist. A subsequent call to icc_get() or icc_set_bw() will traverse or modify these corrupted lists, triggering a slab use afterfree.
KASAN report showing the vulnerability when reproducing via debugfs:
BUG: KASAN: slab-use-after-free in path_find+0x6f8/0xcfc Write of size 8 at addr fff000000d43f748 by task sh/1 ... Call trace: kasan_report+0xac/0xfc path_find+0x6f8/0xcfc icc_get+0x148/0x380 icc_get_set+0xf8/0x2d0 ... Freed by task 1: kfree+0x1a0/0x4a4 icc_get+0x2cc/0x380 icc_get_set+0xf8/0x2d0
Fix this by replacing kfree(path) with the proper teardown function, icc_put(path), which safely removes the requests from the req_list using hlist_del() and drops the provider usage references before freeing the memory.
Additionally, in icc_get(), ensure that the icc_lock mutex is released prior to calling icc_put(path) to avoid a deadlock, as icc_put() internally acquires the same lock.
--- drivers/interconnect/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/interconnect/core.c +4 -3
@@ -526,7 +526,7 @@ struct icc_path *of_icc_get_by_index(str path->name =kasprintf (GFP_KERNEL ,"%s-%s" , src_data->node->name, dst_data->node->name);if (!path->name) { -kfree (path); +icc_put (path); path =ERR_PTR (-ENOMEM ); }
@@ -624,8 +624,9 @@ struct icc_path *icc_get(struct device * path->name =kasprintf (GFP_KERNEL ,"%s-%s" , src_node->name, dst_node->name);if (!path->name) { -kfree (path); - path =ERR_PTR (-ENOMEM ); +mutex_unlock (&icc_lock); +icc_put (path); +return ERR_PTR (-ENOMEM ); } out:mutex_unlock (&icc_lock);
Greg Kroah-Hartman
maintainerPATCH 331/403 ipmi: ipmb: validate write message length
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yousef Alhouseen <[email protected]>
commit 53637506884dbd5c91a89b1a3547d99d80f8ed2c upstream.
ipmb_write() read message fields before validating the length byte.
A zero or short write can read uninitialized stack bytes.
A length smaller than the SMBus header underflows the block write length.
Require a non-empty buffer and the minimum IPMB request length.
Also require the length byte plus payload before parsing the message.
Message-ID: <[email protected]>
--- drivers/char/ipmi/ipmb_dev_int.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/char/ipmi/ipmb_dev_int.c +3 -2
@@ -141,13 +141,14 @@ static ssize_t ipmb_write(struct file *fu8 msg[MAX_MSG_LEN ];ssize_t ret; -if (count >sizeof (msg)) +if (!count || count >sizeof (msg))return -EINVAL ;if (copy_from_user (&msg, buf, count))return -EFAULT ; -if (count < msg[0 ]) +if (msg[IPMB_MSG_LEN_IDX ] <IPMB_REQUEST_LEN_MIN || + count < (size_t )msg[IPMB_MSG_LEN_IDX ] +1 )return -EINVAL ; rq_sa =GET_7BIT_ADDR (msg[RQ_SA_8BIT_IDX ]);
Greg Kroah-Hartman
maintainerPATCH 332/403 ipmi: si: Fix NULL pointer dereference after failed registration
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Seiji Nishikawa <[email protected]>
commit 6d920a75df9a83ab096b3cde7a643b656e4fdfeb upstream.
try_smi_init() allocates new_smi->si_sm and later calls ipmi_register_smi_mod(), which maps to ipmi_add_smi().
During ipmi_add_smi(), the upper IPMI message handler obtains the initial BMC device information through __bmc_get_device_id(). This can fail if the BMC does not return a successful response to the Get Device ID command.
When the BMC returns a nonzero completion code, the device-id helper retries the command and eventually returns -EIO if the device ID still cannot be fetched.
On this failure path, ipmi_add_smi() logs "Unable to get the device id" and goes to out_err_started, where it invokes the lower driver's shutdown callback. try_smi_init() then logs the returned registration failure:
ipmi_si IPI0001:00: IPMI message handler: Unable to get the device id: -5 ipmi_si IPI0001:00: Unable to register device: error -5
For ipmi_si, the shutdown callback is shutdown_smi(), which cleans up the SI state machine data, frees smi_info->si_sm, and sets smi_info->si_sm and smi_info->intf to NULL.
However, intf->in_shutdown is not set on this failed-registration rollback path. Therefore, the asynchronous redo_bmc_reg work item can still retry BMC device-id probing after the lower driver has already cleared its SI state machine data. In the observed case, that retry path reached start_next_msg(), which passed the NULL smi_info->si_sm pointer to the selected KCS state machine handler:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 Workqueue: events redo_bmc_reg [ipmi_msghandler] RIP: start_kcs_transaction+0x2c/0x190 [ipmi_si] Call Trace: start_next_msg+0x50/0x80 [ipmi_si] check_start_timer_thread.part.9+0x3b/0x50 [ipmi_si] sender+0x69/0x80 [ipmi_si] i_ipmi_request+0x2ac/0x9d0 [ipmi_msghandler] __get_device_id.isra.29+0xaa/0x180 [ipmi_msghandler] __bmc_get_device_id+0xef/0x950 [ipmi_msghandler] redo_bmc_reg+0x52/0x60 [ipmi_msghandler] process_one_work+0x1a7/0x360
Set intf->in_shutdown on the out_err_started path before invoking the lower driver's shutdown callback. This prevents later redo_bmc_reg retries from using an interface whose lower driver state has been cleaned up, and applies the same shutdown state to other IPMI interfaces as well.
Message-ID: <[email protected]>
--- drivers/char/ipmi/ipmi_msghandler.c | 1 + 1 file changed, 1 insertion(+)
drivers/char/ipmi/ipmi_msghandler.c +1 -0
@@ -3692,6 +3692,7 @@ int ipmi_add_smi(struct module * out_err_bmc_reg:ipmi_bmc_unregister (intf); out_err_started: + intf->in_shutdown = true;if (intf->handlers->shutdown) intf->handlers->shutdown (intf->send_info); out_err:
Greg Kroah-Hartman
maintainerPATCH 333/403 net/iucv: filter frames in afiucv_hs_rcv() by ingress device
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexandra Winter <[email protected]>
commit 80230a18c164a4b5bbc048fe2768b219ac17bc5a upstream.
afiucv_hs_rcv() selects a socket from iucv_sk_list by matching four 8-byte name fields in the transport header alone. No check is made against the net_device the frame arrived on.
This can cause a frame arriving on any netdev to be delivered to an AF_IUCV socket. Three problems follow.
First, a frame arriving over HiperSockets can be delivered to a socket bound to the classic z/VM IUCV transport, which has iucv->hs_dev == NULL. iucv_sock_bind() takes the classic path whenever the requested userid matches iucv_userid, even on a guest that also has a HiperSockets device carrying the same identifier. The child socket created by afiucv_hs_callback_syn() for such a match inherits hs_dev = NULL and transport = AF_IUCV_TRANS_HIPER, so the first send() on it returns -ENODEV. The socket delivered to accept() is unusable.
Second, a frame arriving on one netdev can be delivered to a socket bound to a different IQD device. Which can lead to
- Accept-queue exhaustion (DoS)
- Attacker-controlled peer identity in the child socket
- Data injection into existing sockets
- Fabric noise on the IQD fabric, where bogus replies are sent
- killing established connections
Third, all AF_IUCV sockets live in init_net, as iucv_sock_alloc() calls sk_alloc(&init_net, ...). But even frames arriving on netdev devices in a namespace can be delivered to an IUCV socket. So a process in an unprivileged user and network namespace holding only the CAP_NET_RAW capability valid within that namespace can send a raw ETH_P_AF_IUCV frame on its own lo device and have it matched against init_net sockets.
Fix all three by skipping any socket whose hs_dev does not match the ingress device. A classic z/VM IUCV socket has hs_dev == NULL; the ingress dev is never NULL, so classic sockets are skipped automatically. An unbound HIPER socket also has hs_dev == NULL and is skipped. A bound HIPER socket is only reachable from the exact IQD device it was bound to. Because hs_dev is always a device in init_net (iucv_sock_bind() scans for_each_netdev_rcu(&init_net, ...) exclusively), a frame whose ingress device belongs to another namespace never matches any socket.
Note that AF_IUCV over HiperSockets provides no per-connection
authentication: no sequence numbers, no TLS, no nonce. The four name fields identifying a connection are exchanged in plaintext on the shared HiperSockets segment (VCHID). Any host on the same HiperSockets segment could spoof any frame type against an existing connection. That is a protocol-level property unchanged by this patch. The fix reduces the attack surface to peers present on the same HiperSockets segment.
--- net/iucv/af_iucv.c | 2 ++ 1 file changed, 2 insertions(+)
net/iucv/af_iucv.c +2 -0
@@ -2078,6 +2078,8 @@ static int afiucv_hs_rcv(struct sk_buff sk =NULL ;read_lock (&iucv_sk_list.lock);sk_for_each (sk, &iucv_sk_list.head) { +if (iucv_sk (sk)->hs_dev != dev) +continue ;if (trans_hdr->flags ==AF_IUCV_FLAG_SYN ) { if ((!memcmp(&iucv_sk(sk)->src_name, trans_hdr->destAppName, 8)) &&
Greg Kroah-Hartman
maintainerPATCH 334/403 xdp: fix zero-copy frame layout
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit 71283aaa6c65b3cec84caf1dc78560985737641f upstream.
xdp_convert_zc_to_xdp_frame() clones an XSK packet into an order-0 page and advertises PAGE_SIZE as its frame size. It allows the copied frame to occupy the page tail needed by skb_shared_info and records zero headroom even when metadata separates the frame header from packet data. An AF_XDP zero-copy packet redirected through cpumap can therefore make the skb overlap skb_shared_info or place it beyond the allocated page.
Limit the copied layout to SKB_WITH_OVERHEAD(PAGE_SIZE) and include the metadata length in frame headroom. Redirect callers already handle a NULL conversion result.
BUG: KASAN: slab-out-of-bounds in skb_gro_receive Write of size 4 at addr ffff88800cf37004 by task cpumap/1/map:1/146 Call Trace: skb_gro_receive (net/core/gro.c:174) udp_gro_receive (net/ipv4/udp_offload.c:812) inet_gro_receive (net/ipv4/af_inet.c:1539) dev_gro_receive (net/core/gro.c:515) gro_receive_skb (net/core/gro.c:633) cpu_map_kthread_run (kernel/bpf/cpumap.c:395) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:164) ret_from_fork_asm (arch/x86/entry/entry_64.S:255) Kernel panic - not syncing: KASAN: panic_on_warn set ...
--- net/core/xdp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/core/xdp.c +2 -2
@@ -606,7 +606,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_ xdp->data - xdp->data_meta; totsize = xdp->data_end - xdp->data + metasize; -if (sizeof (*xdpf) + totsize >PAGE_SIZE ) +if (sizeof (*xdpf) + totsize >SKB_WITH_OVERHEAD (PAGE_SIZE ))return NULL ; page =dev_alloc_page ();
@@ -623,7 +623,7 @@ struct xdp_frame *xdp_convert_zc_to_xdp_ xdpf->data = addr + metasize; xdpf->len = totsize - metasize; - xdpf->headroom =0 ; + xdpf->headroom = metasize; xdpf->metasize = metasize; xdpf->frame_sz =PAGE_SIZE ; xdpf->mem.type =MEM_TYPE_PAGE_ORDER0 ;
Greg Kroah-Hartman
maintainerPATCH 335/403 slip: fix use-after-free in sl_sync()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Khromov <[email protected]>
commit 2c4e7c42d77e78ad595dbb9e4b5886b58b45d89d upstream.
slip_devs[] stores bare net_device pointers and takes no reference on them. sl_sync() and sl_alloc() walk that table from slip_open() under rtnl_lock(), while an entry is dropped by sl_free_netdev(), which sl_setup() installs as dev->priv_destructor.
priv_destructor is called from netdev_run_todo(), which deliberately runs with the RTNL semaphore released so that it can sleep while waiting for the device refcount to drop:
/* Snapshot list, allow later requests */ list_replace_init(&net_todo_list, &list); __rtnl_unlock(); ... if (dev->priv_destructor) dev->priv_destructor(dev); /* slip_devs[i] = NULL */ if (dev->needs_free_netdev) free_netdev(dev); ... /* Free network device */ kobject_put(&dev->dev.kobj);
So rtnl_lock() does not serialise slip_open() against the teardown at all. sl_sync() can load slip_devs[i] while the entry is still published and dereference it after netdev_run_todo() has run the destructor and released the device:
CPU0 (slip_open) CPU1 (slip_close)
unregister_netdev()
rtnl_unlock()
netdev_run_todo()
__rtnl_unlock()
rtnl_lock()
sl_sync()
dev = slip_devs[i]
priv_destructor(dev)
slip_devs[i] = NULL
kobject_put(&dev->dev.kobj)
/* dev is freed */
sl = netdev_priv(dev)
if (sl->tty || sl->leased) /* use-after-free */BUG: KASAN: use-after-free in sl_sync drivers/net/slip/slip.c:730 [inline] BUG: KASAN: use-after-free in slip_open+0xef4/0x1210 drivers/net/slip/slip.c:806 Read of size 1 at addr ffff8880712dac71 by task syz-executor.2/6506 CPU: 2 PID: 6506 Comm: syz-executor.2 Not tainted 6.1.134-syzkaller-00260-g0c8fc3469765 #0 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 Call Trace: sl_sync drivers/net/slip/slip.c:730 [inline] slip_open+0xef4/0x1210 drivers/net/slip/slip.c:806 tty_ldisc_open+0xa2/0x120 drivers/tty/tty_ldisc.c:433 tty_set_ldisc+0x324/0x720 drivers/tty/tty_ldisc.c:564 tiocsetd drivers/tty/tty_io.c:2428 [inline] tty_ioctl+0x5f0/0x1530 drivers/tty/tty_io.c:2712 Allocated by task 6502: alloc_netdev_mqs+0x98/0xfe0 net/core/dev.c:10719 sl_alloc drivers/net/slip/slip.c:756 [inline] slip_open+0x36d/0x1210 drivers/net/slip/slip.c:817 tty_ldisc_open+0xa2/0x120 drivers/tty/tty_ldisc.c:433 tty_set_ldisc+0x324/0x720 drivers/tty/tty_ldisc.c:564 Freed by task 6497: device_release+0xa2/0x240 drivers/base/core.c:2507 kobject_put+0x179/0x280 lib/kobject.c:729 netdev_run_todo+0x6c8/0xef0 net/core/dev.c:10509 slip_close+0x166/0x1c0 drivers/net/slip/slip.c:906 tty_ldisc_close+0x113/0x1a0 drivers/tty/tty_ldisc.c:456 tty_ldisc_kill+0x94/0x160 drivers/tty/tty_ldisc.c:614 tty_ldisc_release+0xe3/0x2b0 drivers/tty/tty_ldisc.c:782 tty_release+0xbcc/0xe70 drivers/tty/tty_io.c:1860
Commit e58c19124189 ("slip: Fix use-after-free Read in slip_open") fixed a different source of stale entries - a device left in slip_devs[] after slip_open() freed it on the registration error path - and does not address this race, which is why the report survives it.
Drop the entry from ndo_uninit instead. unregister_netdevice() calls ndo_uninit under RTNL, before the device is queued to netdev_run_todo(), so an entry that sl_sync() can still see while holding RTNL belongs to a device that cannot be freed until RTNL is dropped. sl_free_netdev() stays only for the slip_open() error path, where register_netdevice() may have failed before ndo_init and ndo_uninit is then not called either. Both running for the same device is harmless: they run under the same RTNL section, so the slot cannot have been reused in between.
This also removes the second symptom of the missing exclusion: a destructor running after sl_alloc() had already handed the slot out to another channel used to clear a live entry, so sl_sync() stopped at that NULL, sl_alloc() returned the same index again, and register_netdevice() failed with -EEXIST because slN was still there.
Reproduced on x86_64 with several threads looping over open("/dev/ptmx") + ioctl(TIOCSETD, N_SLIP) + close().
Found by Linux Verification Center (linuxtesting.org) with Syzkaller.
--- drivers/net/slip/slip.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/slip/slip.c +8 -3
@@ -628,9 +628,15 @@ static void sl_uninit(struct net_devicestruct slip *sl =netdev_priv (dev);sl_free_bufs (sl); +/* Drop the slip_devs[] entry here rather than from the destructor: +* ndo_uninit runs under RTNL, so it cannot race sl_sync(). +*/ + slip_devs[dev->base_addr] =NULL ; } -/* Hook the destructor so we can free slip devices at the right point in time */ +/* Only for the slip_open() error path: register_netdevice() can fail before +* ndo_init, and then ndo_uninit is not called either. +*/ static void sl_free_netdev (struct net_device *dev) {int i = dev->base_addr;
@@ -657,7 +663,6 @@ static void sl_setup(struct net_device * { dev->netdev_ops = &sl_netdev_ops; dev->needs_free_netdev = true; - dev->priv_destructor = sl_free_netdev; dev->hard_header_len =0 ; dev->addr_len =0 ;
@@ -908,7 +913,7 @@ static void slip_close(struct tty_struct#endif /* Flush network side */ unregister_netdev (sl->dev); -/* This will complete via sl_free_netdev */ +/* sl_uninit() has dropped the slip_devs[] entry by now */ }static void slip_hangup (struct tty_struct *tty)
Greg Kroah-Hartman
maintainerPATCH 336/403 net: usb: qmi_wwan: add Telit Cinterion FE990D50 composition
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fabio Porcedda <[email protected]>
commit 1056e79fffd0841f43c6a1b25664b196b3caf1c6 upstream.
Add the followin Telit Cinterion FE990D50 composition:
0x0991: rmnet + tty (AT/NMEA) + tty (AT) + tty (AT) + tty (AT) +
tty (diag) + ADPL + adb
T: Bus=01 Lev=01 Prnt=01 Port=06 Cnt=03 Dev#= 10 Spd=480 MxCh= 0 D: Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=1bc7 ProdID=0991 Rev=06.06 S: Manufacturer=Telit Cinterion S: Product=FE990 S: SerialNumber=2aa802d2 C: #Ifs= 9 Cfg#= 1 Atr=e0 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=88(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=89(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8a(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8b(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 6 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=80 Driver=(none) E: Ad=8c(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 7 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=ff Prot=70 Driver=(none) E: Ad=8d(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 8 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=07(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=8e(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
--- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/usb/qmi_wwan.c +1 -0
@@ -1360,6 +1360,7 @@ static const struct usb_device_id produc {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ {QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */ + {QMI_QUIRK_SET_DTR(0x1bc7, 0x0991, 0)}, /* Telit FE990D50 */ {QMI_QUIRK_SET_DTR(0x1bc7, 0x1031, 3)}, /* Telit LE910C1-EUX */ {QMI_QUIRK_SET_DTR(0x1bc7, 0x1034, 2)}, /* Telit LE910C4-WWX */ {QMI_QUIRK_SET_DTR(0x1bc7, 0x1037, 4)}, /* Telit LE910C4-WWX */
Greg Kroah-Hartman
maintainerPATCH 337/403 net: tun: bound receive headroom
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asim Viladi Oglu Manizada <[email protected]>
commit 447c9303942c439a117d9b76ce6d6e2116b38ee7 upstream.
tun_get_user() uses tun->align both as skb headroom and when choosing how much packet data to keep linear. OVS can propagate an oversized headroom request from another port to TUN or TAP.
When align is larger than the usable space in a one-page skb head, SKB_MAX_HEAD(align) underflows and the result becomes negative when stored in good_linear. That value later wraps when assigned to the size_t linear variable, and tun_alloc_skb() can place skb->data outside the allocated head.
Bound the headroom stored by TUN to the one-page skb-head budget and the largest non-sentinel 16-bit skb header offset. Leave one linear byte for raw TUN and a complete Ethernet header for TAP, including NET_IP_ALIGN.
Also pull the raw-TUN protocol byte and the TAP Ethernet header before accessing them, so these checks remain safe for nonlinear skbs supplied by other allocation paths.
--- drivers/net/tun.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-)
drivers/net/tun.c +16 -5
@@ -1170,11 +1170,16 @@ static netdev_features_t tun_net_fix_feastatic void tun_set_headroom (struct net_device *dev,int new_hr) {struct tun_struct *tun =netdev_priv (dev); +size_t max_headroom; -if (new_hr <NET_SKB_PAD ) - new_hr =NET_SKB_PAD ; + max_headroom =min_t (size_t,SKB_MAX_HEAD (0 ),U16_MAX -1 ); - tun->align = new_hr; +if ((tun->flags &TUN_TYPE_MASK ) ==IFF_TAP ) + max_headroom -=ETH_HLEN +NET_IP_ALIGN ; +else + max_headroom -=1 ; + + tun->align =clamp_t (int, new_hr,NET_SKB_PAD , max_headroom); } static void
@@ -1877,7 +1882,13 @@ static ssize_t tun_get_user(struct tun_sswitch (tun->flags &TUN_TYPE_MASK ) {case IFF_TUN :if (tun->flags &IFF_NO_PI ) { -u8 ip_version = skb->len ? (skb->data[0 ] >>4 ) :0 ; +u8 ip_version; + +if (!pskb_may_pull (skb,1 )) { + err = -EINVAL ; + goto drop; + } + ip_version = skb->data[0 ] >>4 ;switch (ip_version) {case 4 :
@@ -1897,7 +1908,7 @@ static ssize_t tun_get_user(struct tun_s skb->dev = tun->dev;break ;case IFF_TAP : -if (frags && !pskb_may_pull (skb,ETH_HLEN )) { +if (!pskb_may_pull (skb,ETH_HLEN )) { err = -ENOMEM ; drop_reason =SKB_DROP_REASON_HDR_TRUNC ; goto drop;
Greg Kroah-Hartman
maintainerPATCH 338/403 net: dsa: realtek: use gpiod_set_value_cansleep for reset GPIO
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ahmad Fatoum <[email protected]>
commit fb58b6a696b30bcbfbe0cfc0a91b19c816a955fc upstream.
rtl83xx_reset_assert() and rtl83xx_reset_deassert() are only called from the probe path, which may sleep and is not timing-critical. When the reset GPIO is provided by a sleeping controller such as an I2C I/O expander, gpiod_set_value() warns:
WARNING: drivers/gpio/gpiolib.c:4030 at gpiod_set_value+0x44/0x80, CPU#1: kworker/u16:4/61 Hardware name: B&O MAP CA33 Rev f (UNKNOWN) (DT) Workqueue: events_unbound deferred_probe_work_func pc : gpiod_set_value+0x44/0x80 lr : rtl83xx_probe+0x1d8/0x3a0 Call trace: gpiod_set_value+0x44/0x80 (P) rtl83xx_probe+0x1d8/0x3a0 realtek_mdio_probe+0x24/0xa0 mdio_probe+0x38/0x78 really_probe+0xc4/0x3e0 __driver_probe_device+0x15c/0x1b8 driver_probe_device+0xb4/0x120 __device_attach_driver+0xb8/0x1a0 bus_for_each_drv+0x88/0xf0 __device_attach+0xa0/0x1d8 device_initial_probe+0x54/0x68 bus_probe_device+0x38/0xa0 deferred_probe_work_func+0xb8/0x120 process_one_work+0x184/0x4e8 worker_thread+0x188/0x308 kthread+0x130/0x150 ret_from_fork+0x10/0x20
Switch both helpers to gpiod_set_value_cansleep() so such a reset GPIO can be used without triggering the warning.
The reset GPIO has been driven with the non-sleeping gpiod_set_value() since the driver was added in v4.19. The call has since been refactored across several files - from realtek-smi.c / realtek-mdio.c into the common rtl83xx.c module and then into the rtl83xx_reset_assert() and rtl83xx_reset_deassert() helpers (both in v6.9). This patch therefore applies as-is only to kernels that carry those helpers (v6.9+); older stable kernels need the same gpiod_set_value_cansleep() conversion at the corresponding open-coded call sites.
--- drivers/net/dsa/realtek/rtl83xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/dsa/realtek/rtl83xx.c +2 -2
@@ -309,7 +309,7 @@ void rtl83xx_reset_assert(struct realtek"Failed to assert the switch reset control: %pe\n" , ERR_PTR(ret)); -gpiod_set_value (priv->reset, true); +gpiod_set_value_cansleep (priv->reset, true); }void rtl83xx_reset_deassert (struct realtek_priv *priv)
@@ -322,7 +322,7 @@ void rtl83xx_reset_deassert(struct realt"Failed to deassert the switch reset control: %pe\n" , ERR_PTR(ret)); -gpiod_set_value (priv->reset, false); +gpiod_set_value_cansleep (priv->reset, false); }MODULE_AUTHOR ("Luiz Angelo Daros de Luca <[email protected]>" );
Greg Kroah-Hartman
maintainerPATCH 339/403 net: ipa: fix stalled modem TX queue after runtime resume
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jorijn van der Graaf <[email protected]>
commit 3cbfd627ee720f3d2460d2cbe2fe9e4130240db6 upstream.
ipa_start_xmit() unconditionally stops the TX queue before calling pm_runtime_get(), relying on the wake scheduled by runtime resume (ipa_modem_wake_queue_work()) to restart it once power is ACTIVE. But that work is queued from within the runtime resume callback, before the device's power state reaches RPM_ACTIVE, so it can run while the device is still RPM_RESUMING. The wake is then consumed too early: the transmit it restarts stops the queue again, pm_runtime_get() returns -EINPROGRESS without arranging any future wake (deferred_resume exists only for RPM_SUSPENDING), and after the resume completes nothing is left to wake the queue. Transmit stalls
permanently: packets pile up in the qdisc behind the stopped queue, the device runtime-suspends, and since the netdev registers no ndo_tx_timeout the watchdog never fires. Observed on SM7635 (Fairphone 6) as the cellular data path going permanently deaf within hours, RX included, since nothing resumes the suspended endpoints.
Close the window by making the wake work wait for the resume to complete (pm_runtime_get_sync()) before waking the queue. Every queue stop is then guaranteed a later wake that happens while power is ACTIVE; a transmit racing a new suspend/resume cycle re-schedules the work. If the device could not be resumed, wake the queue anyway so pending packets are dropped by the transmit path rather than stranded.
The STARTED power flag used to narrow this window: a wake running before the transmit path's stop suppressed that stop, but only once, as the flag was cleared by the first stop it absorbed. Removing the flag made a single transmit during an in-flight resume sufficient to strand the queue, which is the form observed.
With an accelerated reproducer (autosuspend delay shortened to 5 ms, ~20 packets/s of TX), an unpatched kernel stalled three times in 230 s / 4380 packets; with this patch the same test ran 3601 s / 70298 packets without a stall.
--- drivers/net/ipa/ipa_modem.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
drivers/net/ipa/ipa_modem.c +17 -1
@@ -269,13 +269,29 @@ void ipa_modem_suspend(struct net_device* the modem. We can't enable the queue directly in ipa_modem_resume() * because transmits restart the instant the queue is awakened; but the * device power state won't be ACTIVE until *after* ipa_modem_resume() -* returns. +* returns. A transmit restarted before that would stop the queue +* again and get -EINPROGRESS from pm_runtime_get(), and with this +* work having already run, nothing would ever wake the queue again. +* So wait for the resume to complete before waking the queue. */ static void ipa_modem_wake_queue_work (struct work_struct *work) { struct ipa_priv *priv = container_of(work, struct ipa_priv, work); +struct device *dev = priv->ipa->dev; +int ret; + ret =pm_runtime_get_sync (dev); + +/* Wake the queue even if the device could not be resumed, so +* that pending packets are dropped by the transmit path rather +* than stranded behind a stopped queue. +*/ netif_wake_queue (priv->tx->netdev); + +if (ret <0 ) +pm_runtime_put_noidle (dev); +else + (void )pm_runtime_put_autosuspend (dev); }/** ipa_modem_resume() - resume callback for runtime_pm
Greg Kroah-Hartman
maintainerPATCH 340/403 net: l2tp: do not propagate multicast notification errors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zihan Xi <[email protected]>
commit af20e269f7459d2ce69887fdf2fad7caf986c865 upstream.
The tunnel create, tunnel modify, session create, and session modify netlink handlers send multicast notifications through helpers that can fail while allocating or encoding a message, or while multicasting it.
For tunnel and session create/modify, a notification is sent after the live operation has completed. Returning a best-effort notification error as the command result can therefore report failure for an operation that already committed and can cause callers to retry and accumulate live objects.
Keep sending notifications for listener visibility, but do not propagate their best-effort status as the command result. This also keeps the tunnel modify command consistent with the other notification-only paths.
--- net/l2tp/l2tp_netlink.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
net/l2tp/l2tp_netlink.c +8 -8
@@ -248,8 +248,8 @@ static int l2tp_nl_cmd_tunnel_create(strkfree (tunnel); goto out; } - ret =l2tp_tunnel_notify (&l2tp_nl_family, info, tunnel, -L2TP_CMD_TUNNEL_CREATE ); +l2tp_tunnel_notify (&l2tp_nl_family, info, tunnel, +L2TP_CMD_TUNNEL_CREATE );l2tp_tunnel_put (tunnel); out:
@@ -305,8 +305,8 @@ static int l2tp_nl_cmd_tunnel_modify(str goto out; } - ret =l2tp_tunnel_notify (&l2tp_nl_family, info, - tunnel,L2TP_CMD_TUNNEL_MODIFY ); +l2tp_tunnel_notify (&l2tp_nl_family, info, + tunnel,L2TP_CMD_TUNNEL_MODIFY );l2tp_tunnel_put (tunnel);
@@ -645,8 +645,8 @@ static int l2tp_nl_cmd_session_create(st session =l2tp_session_get (net, tunnel->sock, tunnel->version, tunnel_id, session_id);if (session) { - ret =l2tp_session_notify (&l2tp_nl_family, info, session, -L2TP_CMD_SESSION_CREATE ); +l2tp_session_notify (&l2tp_nl_family, info, session, +L2TP_CMD_SESSION_CREATE );l2tp_session_put (session); } }
@@ -710,8 +710,8 @@ static int l2tp_nl_cmd_session_modify(stif (info->attrs[L2TP_ATTR_RECV_TIMEOUT ]) session->reorder_timeout =nla_get_msecs (info->attrs[L2TP_ATTR_RECV_TIMEOUT ]); - ret =l2tp_session_notify (&l2tp_nl_family, info, - session,L2TP_CMD_SESSION_MODIFY ); +l2tp_session_notify (&l2tp_nl_family, info, + session,L2TP_CMD_SESSION_MODIFY );l2tp_session_put (session);
Greg Kroah-Hartman
maintainerPATCH 341/403 net: openvswitch: fix flow mask use-after-free on flow deletion
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Maximets <[email protected]>
commit 4e30317ff67a2eb12b4d890d39f72fd7e7117d48 upstream.
The commit in the Fixes tag below made so flow->mask free is scheduled via RCU right after it is removed from the flow table. The pointer stays in the flow structure and it can be accessible while in the same RCU critical section. This is done to avoid requiring ovs_mutex for the ovs_flow_free().
However, while removing the flow during processing of CMD_DEL, we do not take RCU read lock before the removal, and ovs_flow_cmd_fill_info() uses the flow->mask pointer afterwards. The RCU read lock is taken, but it's already late at that point. The comment on that line acknowledges that the lock is cosmetic and doesn't serve a real purpose.
This leads to use-after-free if the RCU grace period passes between removal and the filling. It is a short race window, but it is there and can lead to a real crash in case memory allocation for the info takes a bit longer:
BUG: KASAN: slab-use-after-free in __ovs_nla_put_key
net/openvswitch/flow_netlink.c:1996
BUG: KASAN: slab-use-after-free in ovs_nla_put_key+0x2463/0x2e30
net/openvswitch/flow_netlink.c:2250
Read of size 4 at addr ffff88801ee89970 by task ovs_flow_del_ec/9487
Call Trace:
<TASK>
__ovs_nla_put_key net/openvswitch/flow_netlink.c:1996
ovs_nla_put_key+0x2463/0x2e30 net/openvswitch/flow_netlink.c:2250
ovs_flow_cmd_fill_info+0x420/0x9c0 net/openvswitch/datapath.c:930
ovs_flow_cmd_del+0x53a/0x970 net/openvswitch/datapath.c:1467
...
netlink_rcv_skb+0x156/0x420 net/netlink/af_netlink.c:2556
</TASK>
Allocated by task 9487:
mask_alloc net/openvswitch/flow_table.c:967
flow_mask_insert net/openvswitch/flow_table.c:1012
ovs_flow_tbl_insert+0xea2/0x1a90 net/openvswitch/flow_table.c:1084
ovs_flow_cmd_new+0x7e3/0xd90 net/openvswitch/datapath.c:1086
...
netlink_rcv_skb+0x156/0x420 net/netlink/af_netlink.c:2556
Freed by task 9485:
rcu_free_sheaf+0x1e/0x100 mm/slub.c:5978
rcu_do_batch kernel/rcu/tree.c:2645
rcu_core+0x59c/0x10c0 kernel/rcu/tree.c:2897
handle_softirqs+0x1e4/0x9a0 kernel/softirq.c:622
...
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1062ovs_flow_tbl_remove() must be called after the ovs_flow_cmd_fill_info() to avoid this race. This also helps with cleaning up the forced cast and the cosmetic RCU read lock. Before the commit in the Fixes tag the order did not matter as long as the flow object itself was not freed.
A wider RCU critical section could be another option, but we have a GFP_KERNEL allocation in the way.
Reported by Trend Micro's Zero Day Initiative as ZDI-CAN-32042.
--- net/openvswitch/datapath.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-)
net/openvswitch/datapath.c +23 -22
@@ -1442,33 +1442,34 @@ static int ovs_flow_cmd_del(struct sk_bu goto unlock; } -ovs_flow_tbl_remove (&dp->table, flow); -ovs_unlock (); - - reply =ovs_flow_cmd_alloc_info ((const struct sw_flow_actions __force *) flow->sf_acts, + reply =ovs_flow_cmd_alloc_info (ovsl_dereference (flow->sf_acts), &flow->id, info, false, ufid_flags); -if (likely (reply)) { -if (!IS_ERR (reply)) { -rcu_read_lock ();/*To keep RCU checker happy. */ - err =ovs_flow_cmd_fill_info (flow, ovs_header->dp_ifindex, - reply, info->snd_portid, - info->snd_seq,0 , -OVS_FLOW_CMD_DEL , - ufid_flags); -rcu_read_unlock (); -if (WARN_ON_ONCE (err <0 )) { -kfree_skb (reply); - goto out_free; - } +if (IS_ERR (reply)) { +netlink_set_err (sock_net (skb->sk)->genl_sock,0 ,0 , +PTR_ERR (reply)); + reply =NULL ; + } -ovs_notify (&dp_flow_genl_family, reply, info); - }else { -netlink_set_err (sock_net (skb->sk)->genl_sock,0 ,0 , -PTR_ERR (reply)); +if (likely (reply)) { + err =ovs_flow_cmd_fill_info (flow, ovs_header->dp_ifindex, + reply, info->snd_portid, + info->snd_seq,0 , +OVS_FLOW_CMD_DEL , ufid_flags); +if (WARN_ON_ONCE (err <0 )) { +kfree_skb (reply); + reply =NULL ; } } +/* Removal has to happen after ovs_flow_cmd_fill_info(), as it uses +* the flow->mask that can be scheduled to be freed by the +* ovs_flow_tbl_remove() and we're not holding the RCU read lock. +*/ +ovs_flow_tbl_remove (&dp->table, flow); +ovs_unlock (); + +if (likely (reply)) +ovs_notify (&dp_flow_genl_family, reply, info); -out_free:ovs_flow_free (flow, true);return 0 ; unlock:
Greg Kroah-Hartman
maintainerPATCH 342/403 net: openvswitch: fix nf_connlabels leak in ovs_ct_init
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruoyu Wang <[email protected]>
commit f9de5db270a4c2641de87ee558c16a9bc6eb4cd8 upstream.
ovs_ct_init() acquires a connlabels reference before initializing the conntrack limit state. If ovs_ct_limit_init() fails, its error is returned directly. The pernet core does not invoke the exit callback for the operation whose initialization failed, so ovs_ct_exit() cannot drop the reference.
This leaves labels_used elevated when Open vSwitch pernet registration fails for an existing network namespace. Subsequent conntrack entries in that namespace may allocate label extensions even though Open vSwitch failed to register.
Drop the connlabels reference before returning a conntrack limit initialization error. ovs_ct_limit_init() already releases its partial state, and the original error remains unchanged.
This issue was found by a static analysis checker and confirmed by manual source review.
--- net/openvswitch/conntrack.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
net/openvswitch/conntrack.c +5 -3
@@ -2003,6 +2003,7 @@ int ovs_ct_init(struct net *net) {unsigned int n_bits =sizeof (struct ovs_key_ct_labels ) *BITS_PER_BYTE ;struct ovs_net *ovs_net =net_generic (net, ovs_net_id); +int err =0 ;if (nf_connlabels_get (net, n_bits -1 )) { ovs_net->xt_label = false;
@@ -2012,10 +2013,11 @@ int ovs_ct_init(struct net *net) }#if IS_ENABLED (CONFIG_NETFILTER_CONNCOUNT ) -return ovs_ct_limit_init (net, ovs_net); -#else -return 0 ; + err =ovs_ct_limit_init (net, ovs_net); +if (err && ovs_net->xt_label) +nf_connlabels_put (net);#endif +return err; }void ovs_ct_exit (struct net *net)
Greg Kroah-Hartman
maintainerPATCH 343/403 net: ravb: avoid dereferencing an invalid PTP clock
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
commit 1f77af0aaf277413ff32f6ff8c2c4282bd64c897 upstream.
The PTP clock is unavailable before the first open, so querying its index can dereference a NULL pointer. Registration failures can also leave an error pointer in priv->ptp.clock.
Cache the PHC index separately and report -1 while no clock is registered. Normalize registration errors to NULL and preserve the static timestamping capabilities.
--- drivers/net/ethernet/renesas/ravb.h | 1 + drivers/net/ethernet/renesas/ravb_main.c | 3 ++- drivers/net/ethernet/renesas/ravb_ptp.c | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-)
drivers/net/ethernet/renesas/ravb.h +1 -0
@@ -1034,6 +1034,7 @@ struct ravb_ptp_perout {struct ravb_ptp {struct ptp_clock *clock;struct ptp_clock_info info; +int phc_index;u32 default_addend;u32 current_addend;int extts[N_EXT_TS ];
drivers/net/ethernet/renesas/ravb_main.c +2 -1
@@ -1761,7 +1761,7 @@ static int ravb_get_ts_info(struct net_d (1 <<HWTSTAMP_FILTER_NONE ) | (1 <<HWTSTAMP_FILTER_PTP_V2_L2_EVENT ) | (1 <<HWTSTAMP_FILTER_ALL ); - info->phc_index =ptp_clock_index (priv->ptp.clock); + info->phc_index =READ_ONCE (priv->ptp.phc_index); }return 0 ;
@@ -2953,6 +2953,7 @@ static int ravb_probe(struct platform_de priv->rstc = rstc; priv->ndev = ndev; priv->pdev = pdev; + priv->ptp.phc_index =-1 ; priv->num_tx_ring[RAVB_BE ] =BE_TX_RING_SIZE ; priv->num_rx_ring[RAVB_BE ] =BE_RX_RING_SIZE ;if (info->nc_queues) {
drivers/net/ethernet/renesas/ravb_ptp.c +13 -2
@@ -325,6 +325,7 @@ void ravb_ptp_interrupt(struct net_devicvoid ravb_ptp_init (struct net_device *ndev,struct platform_device *pdev) {struct ravb_private *priv =netdev_priv (ndev); +struct ptp_clock *clock;unsigned long flags; priv->ptp.info = ravb_ptp_info;
@@ -337,7 +338,15 @@ void ravb_ptp_init(struct net_device *ndravb_modify (ndev,GCCR ,GCCR_TCSS ,GCCR_TCSS_ADJGPTP );spin_unlock_irqrestore (&priv->lock, flags); - priv->ptp.clock =ptp_clock_register (&priv->ptp.info, &pdev->dev); + clock =ptp_clock_register (&priv->ptp.info, &pdev->dev); +if (IS_ERR (clock)) { +netdev_err (ndev,"failed to register PTP clock: %pe\n" , clock); + clock =NULL ; + } + + priv->ptp.clock = clock; +if (clock) +WRITE_ONCE (priv->ptp.phc_index,ptp_clock_index (clock)); }void ravb_ptp_stop (struct net_device *ndev)
@@ -347,5 +356,7 @@ void ravb_ptp_stop(struct net_device *ndravb_write (ndev,0 ,GIC );ravb_write (ndev,0 ,GIS ); -ptp_clock_unregister (priv->ptp.clock); +WRITE_ONCE (priv->ptp.phc_index,-1 ); +if (priv->ptp.clock) +ptp_clock_unregister (priv->ptp.clock); }
Greg Kroah-Hartman
maintainerPATCH 344/403 net: ravb: serialize PTP clock teardown
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqiang Luo <[email protected]>
commit 1cb9663789c5b7a12fcd419fcca6d6254c398252 upstream.
ravb_ptp_interrupt() can race with ravb_ptp_stop() and pass the clock to ptp_clock_event() while ptp_clock_unregister() is freeing it. This can lead to a use-after-free.
Use READ_ONCE() and WRITE_ONCE() for lockless access to the clock pointer. Atomically detach it with xchg() before disabling PTP interrupts, then synchronize all IRQs which can invoke ravb_ptp_interrupt() before unregistering the detached clock.
A handler which read the old pointer completes before the clock is unregistered, while later handlers read NULL and skip the event.
--- drivers/net/ethernet/renesas/ravb.h | 2 + drivers/net/ethernet/renesas/ravb_main.c | 6 +++-- drivers/net/ethernet/renesas/ravb_ptp.c | 37 ++++++++++++++++++++++++------- 3 files changed, 35 insertions(+), 10 deletions(-)
drivers/net/ethernet/renesas/ravb.h +2 -0
@@ -1128,6 +1128,8 @@ struct ravb_private {int msg_enable;int speed;int emac_irq; +int err_irq; +int mgmt_irq; unsigned no_avb_link:1; unsigned avb_link_active_low:1;
drivers/net/ethernet/renesas/ravb_main.c +4 -2
@@ -2887,11 +2887,13 @@ static int ravb_setup_irqs(struct ravb_preturn error;if (info->err_mgmt_irqs) { - error =ravb_setup_irq (priv,"err_a" ,"err_a" ,NULL , ravb_multi_interrupt); + error =ravb_setup_irq (priv,"err_a" ,"err_a" , &priv->err_irq, + ravb_multi_interrupt);if (error)return error; - error =ravb_setup_irq (priv,"mgmt_a" ,"mgmt_a" ,NULL , ravb_multi_interrupt); + error =ravb_setup_irq (priv,"mgmt_a" ,"mgmt_a" , &priv->mgmt_irq, + ravb_multi_interrupt);if (error)return error; }
drivers/net/ethernet/renesas/ravb_ptp.c +29 -8
@@ -299,16 +299,17 @@ static const struct ptp_clock_info ravb_void ravb_ptp_interrupt (struct net_device *ndev) {struct ravb_private *priv =netdev_priv (ndev); +struct ptp_clock *clock =READ_ONCE (priv->ptp.clock);u32 gis =ravb_read (ndev,GIS ); gis &=ravb_read (ndev,GIC ); -if (gis &GIS_PTCF ) { +if ((gis &GIS_PTCF ) && clock) {struct ptp_clock_event event; event.type =PTP_CLOCK_EXTTS ; event.index =0 ; event.timestamp =ravb_read (ndev,GCPT ); -ptp_clock_event (priv->ptp.clock, &event); +ptp_clock_event (clock, &event); }if (gis &GIS_PTMF ) {struct ravb_ptp_perout *perout = priv->ptp.perout;
@@ -344,19 +345,39 @@ void ravb_ptp_init(struct net_device *nd clock =NULL ; } - priv->ptp.clock = clock; +WRITE_ONCE (priv->ptp.clock, clock);if (clock)WRITE_ONCE (priv->ptp.phc_index,ptp_clock_index (clock)); } -void ravb_ptp_stop (struct net_device *ndev) +static void ravb_ptp_disable (struct net_device *ndev) { -struct ravb_private *priv =netdev_priv (ndev); -ravb_write (ndev,0 ,GIC );ravb_write (ndev,0 ,GIS ); +} + +static void ravb_ptp_sync_irqs (struct net_device *ndev) +{ +struct ravb_private *priv =netdev_priv (ndev); + +synchronize_irq (ndev->irq); +if (priv->info->err_mgmt_irqs) { +synchronize_irq (priv->err_irq); +synchronize_irq (priv->mgmt_irq); + } +} + +void ravb_ptp_stop (struct net_device *ndev) +{ +struct ravb_private *priv =netdev_priv (ndev); +struct ptp_clock *clock;WRITE_ONCE (priv->ptp.phc_index,-1 ); -if (priv->ptp.clock) -ptp_clock_unregister (priv->ptp.clock); + clock =xchg (&priv->ptp.clock,NULL ); + +ravb_ptp_disable (ndev); +ravb_ptp_sync_irqs (ndev); + +if (clock) +ptp_clock_unregister (clock); }
Greg Kroah-Hartman
maintainerPATCH 345/403 net: thunderbolt: Release the Rx HopID that was handed out on mismatch
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Ye <[email protected]>
commit 2f1463554d0561a2fead81e3888604e5c1125e29 upstream.
tb_xdomain_alloc_in_hopid() passes the wanted HopID to ida_alloc_range() as the lower bound, so a taken id is not an error there: the allocator returns the next free one above it. tbnet_connected_work() asks for the peer's transmit path, treats any other id as a failure and returns without releasing what it got, so that allocation stays live for the rest of the XDomain connection with nothing left holding a reference to it.
Release the id when it is not the one we asked for, the same way the error unwind at the end of the function releases the expected one.
--- drivers/net/thunderbolt/main.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/net/thunderbolt/main.c +2 -0
@@ -650,6 +650,8 @@ static void tbnet_connected_work(struct ret =tb_xdomain_alloc_in_hopid (net->xd, net->remote_transmit_path);if (ret != net->remote_transmit_path) {netdev_err (net->dev,"failed to allocate Rx HopID\n" ); +if (ret >=0 ) +tb_xdomain_release_in_hopid (net->xd, ret);return ; }
Greg Kroah-Hartman
maintainerPATCH 346/403 net: thunderbolt: Mark the connection down when bringing it up fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Ye <[email protected]>
commit 3c8b26ebf525ba5960510f48c6e9936a79ebe76f upstream.
Every failure path in tbnet_connected_work() undoes its own work and returns without clearing login_sent, so the connection still looks established. The next tbnet_tear_down() therefore takes its main branch and repeats a teardown that already happened: it stops rings that are already stopped, which is a dev_WARN() and fatal under panic_on_warn, and it releases net->remote_transmit_path even on the HopID mismatch path, where this connection never owned that id, silently freeing one that someone else is still using.
Clear login_sent on those paths. That is enough for tbnet_tear_down() to leave the unwound state alone, and login_received has to stay set: it records that the peer has logged in and carries the transmit path it gave us, which nothing on this side can make the peer send again. Two things change beyond keeping the teardown out of the way: the logout request in that block is no longer sent, and the peer's next login request now re-queues our login work rather than connected_work, giving the connection a fresh login instead of a retry on stale state.
--- drivers/net/thunderbolt/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/net/thunderbolt/main.c +10 -0
@@ -629,6 +629,14 @@ static int tbnet_alloc_tx_buffers(structreturn 0 ; } +static void tbnet_connect_failed (struct tbnet *net) +{ +/* Leave login_received set: only the peer can make it true again. */ +mutex_lock (&net->connection_lock); + net->login_sent = false; +mutex_unlock (&net->connection_lock); +} +static void tbnet_connected_work (struct work_struct *work) {struct tbnet *net =container_of (work,typeof (*net), connected_work);
@@ -652,6 +660,7 @@ static void tbnet_connected_work(structnetdev_err (net->dev,"failed to allocate Rx HopID\n" );if (ret >=0 )tb_xdomain_release_in_hopid (net->xd, ret); +tbnet_connect_failed (net);return ; }
@@ -696,6 +705,7 @@ err_stop_rings:tb_ring_stop (net->rx_ring.ring);tb_ring_stop (net->tx_ring.ring);tb_xdomain_release_in_hopid (net->xd, net->remote_transmit_path); +tbnet_connect_failed (net); }static void tbnet_login_work (struct work_struct *work)
Greg Kroah-Hartman
maintainerPATCH 347/403 NTB: ntb_transport: Recycle TX entries before client callbacks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit 256496397287334a19ed80ec7be92bffcae76b9d upstream.
ntb_tx_copy_callback() invokes the client callback before returning the entry to tx_free_q. The callback may wake a stopped client queue, only for the next enqueue to find no local entry and return -EBUSY. The window is narrow, but the retry is unnecessary.
Save the callback data and length, then return the entry to tx_free_q before invoking the client. A completion callback then means both the client buffer and transport entry are ready for reuse.
--- drivers/ntb/ntb_transport.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-)
drivers/ntb/ntb_transport.c +14 -9
@@ -1747,9 +1747,16 @@ static void ntb_transport_rxc_db(unsignestatic void ntb_tx_copy_callback (void *data,const struct dmaengine_result *res) { +struct ntb_payload_header __iomem *hdr;struct ntb_queue_entry *entry = data; -struct ntb_transport_qp *qp = entry->qp; -struct ntb_payload_header __iomem *hdr = entry->tx_hdr; +struct ntb_transport_qp *qp; +unsigned int len; +void *cb_data; + + qp = entry->qp; + hdr = entry->tx_hdr; + cb_data = entry->cb_data; + len = entry->len;/* we need to check DMA results if we are using DMA */ if (res) {
@@ -1789,15 +1796,13 @@ static void ntb_tx_copy_callback(void *d* "link down" or similar. Since no payload is being sent in these * cases, there is nothing to add to the completion queue. */ -if (entry->len >0 ) { - qp->tx_bytes += entry->len; - -if (qp->tx_handler) - qp->tx_handler (qp, qp->cb_data, entry->cb_data, - entry->len); - } +if (len >0 ) + qp->tx_bytes += len;ntb_list_add (&qp->ntb_tx_free_q_lock, &entry->entry, &qp->tx_free_q); + +if (len >0 && qp->tx_handler) + qp->tx_handler (qp, qp->cb_data, cb_data, len); }static void ntb_memcpy_tx (struct ntb_queue_entry *entry,void __iomem *offset)
Greg Kroah-Hartman
maintainerPATCH 348/403 NTB: ntb_transport: Fail TX enqueue when the QP link is down
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit 873ce713fef5dde0939220f04f3484ec86a16fba upstream.
Commit f195a1a6fe41 ("ntb: Drop packets when qp link is down") meant to make ntb_transport_tx_enqueue() drop packets submitted while the QP link is down, but it only returns 0 without consuming the packet. Zero means success by this function's contract, so ntb_netdev reports NETDEV_TX_OK and forgets the skb: nothing queued it, nothing frees it, and it leaks, one skb for every transmit racing a link-down.
Return -ENOLINK instead, restoring the contract that a non-zero return leaves the buffer owned by the caller. With the preceding patch, ntb_netdev frees the skb on non-retryable enqueue failures and returns NETDEV_TX_OK, so a packet racing with link-down is dropped without leaking or entering a busy retry loop.
--- drivers/ntb/ntb_transport.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/ntb/ntb_transport.c +1 -2
@@ -2307,9 +2307,8 @@ int ntb_transport_tx_enqueue(struct ntb_if (!qp || !len)return -EINVAL ; -/* If the qp link is down already, just ignore. */ if (!qp->link_is_up) -return 0 ; +return -ENOLINK ; entry =ntb_list_rm (&qp->ntb_tx_free_q_lock, &qp->tx_free_q);if (!entry) {
Re: [PATCH 6.12 348/403] NTB: ntb_transport: Fail TX enqueue when the QP link is down
HI Greg/Sasha,
Quoted 35 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit
873ce713fef5dde0939220f04f3484ec86a16fbaupstream.Commit
f195a1a6fe41("ntb: Drop packets when qp link is down") meant to makentb_transport_tx_enqueue()drop packets submitted while the QP link is down, but it only returns 0 without consuming the packet. Zero means success by this function's contract, sontb_netdevreportsNETDEV_TX_OKand forgets the skb: nothing queued it, nothing frees it, and it leaks, one skb for every transmit racing a link-down.Return
-ENOLINKinstead, restoring the contract that a non-zero return leaves the buffer owned by the caller. With the preceding patch,ntb_netdevfrees the skb on non-retryable enqueue failures and returnsNETDEV_TX_OK, so a packet racing with link-down is dropped without leaking or entering a busy retry loop.Fixes f195a1a6fe41 ("ntb: Drop packets when qp link is down")Signed-off-by Koichiro Den <[email protected]>Reviewed-by Dave Jiang <[email protected]>Signed-off-by Jakub Kicinski <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/ntb/ntb_transport.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/ntb/ntb_transport.c int ntb_transport_tx_enqueue(struct ntb_ · line 2311
Quoted 7 lines
drivers/ntb/ntb_transport.c +1
-2@@ -2307,9 +2307,8 @@ int ntb_transport_tx_enqueue(struct ntb_if (!qp || !len)return -EINVAL ; -/* If the qp link is down already, just ignore. */ if (!qp->link_is_up) -return 0 ; +return -ENOLINK ;
I have run an AI-assisted backport review and it spotted an issue.
Upstream commit: 873ce713fef5 ("NTB: ntb_transport: Fail TX enqueue
when the QP link is down") returns -ENOLINK when the QP link is down.
commit: 8aaa47351db0 ("net: ntb_netdev: Fix TX busy and drop handling"), makes ntb_netdev treat only -EAGAIN and -EBUSY as retryable. Permanent errors use:
dev_kfree_skb_any(skb);
ndev->stats.tx_dropped++;
return NETDEV_TX_OK;The 6.12.y caller instead sends every nonzero return to:
ndev->stats.tx_dropped++;
ndev->stats.tx_errors++;
return NETDEV_TX_BUSY;It neither frees the skb nor stops the queue. After commit: 6c34659c7712 ("NTB: ntb_transport: Fail TX enqueue when the QP link is down"), -ENOLINK therefore becomes a permanent busy/retry path instead of the intended one-time drop.
I think 6.12.y misses commit: 8aaa47351db0 ("net: ntb_netdev: Fix TX busy and drop handling"). So we should either pull in the prerequisite or drop this.
Thoughts?
thanks,
Harshit
Quoted 6 lines
drivers/ntb/ntb_transport.c +0
-0entry =ntb_list_rm (&qp->ntb_tx_free_q_lock, &qp->tx_free_q);if (!entry) {
Koichiro Den
Reviewed-by Dave JiangRe: [PATCH 6.12 348/403] NTB: ntb_transport: Fail TX enqueue when the QP link is down
Quoted 63 lines · Harshit Mogalapalli
HI Greg/Sasha,
Quoted 35 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit
873ce713fef5dde0939220f04f3484ec86a16fbaupstream.Commit
f195a1a6fe41("ntb: Drop packets when qp link is down") meant to makentb_transport_tx_enqueue()drop packets submitted while the QP link is down, but it only returns 0 without consuming the packet. Zero means success by this function's contract, sontb_netdevreportsNETDEV_TX_OKand forgets the skb: nothing queued it, nothing frees it, and it leaks, one skb for every transmit racing a link-down.Return
-ENOLINKinstead, restoring the contract that a non-zero return leaves the buffer owned by the caller. With the preceding patch,ntb_netdevfrees the skb on non-retryable enqueue failures and returnsNETDEV_TX_OK, so a packet racing with link-down is dropped without leaking or entering a busy retry loop.Fixes f195a1a6fe41 ("ntb: Drop packets when qp link is down")Signed-off-by Koichiro Den <[email protected]>Reviewed-by Dave Jiang <[email protected]>Signed-off-by Jakub Kicinski <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/ntb/ntb_transport.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)drivers/ntb/ntb_transport.c int ntb_transport_tx_enqueue(struct ntb_ · line 2310
Quoted 6 lines
drivers/ntb/ntb_transport.c +1
-2@@ -2307,9 +2307,8 @@ int ntb_transport_tx_enqueue(struct ntb_if (!qp || !len)return -EINVAL ; -/* If the qp link is down already, just ignore. */ if (!qp->link_is_up) -return 0 ; +return -ENOLINK ;I have run an AI-assisted backport review and it spotted an issue.
Upstream commit:
873ce713fef5("NTB:ntb_transport: Fail TX enqueue when the QP link is down") returns-ENOLINKwhen the QP link is down.
commit:8aaa47351db0("net:ntb_netdev: Fix TX busy and drop handling"), makesntb_netdevtreat only-EAGAINand-EBUSYas retryable. Permanent errors use:dev_kfree_skb_any(skb); ndev->stats.tx_dropped++; return NETDEV_TX_OK;The 6.12.y caller instead sends every nonzero return to:
ndev->stats.tx_dropped++; ndev->stats.tx_errors++; return NETDEV_TX_BUSY;
Quoted 8 lines
It neither frees the skb nor stops the queue. After commit:
6c34659c7712("NTB:ntb_transport: Fail TX enqueue when the QP link is down"),-ENOLINKtherefore becomes a permanent busy/retry path instead of the intended one-time drop.I think 6.12.y misses commit:
8aaa47351db0("net:ntb_netdev: Fix TX busy and drop handling"). So we should either pull in the prerequisite or drop this.
Hi Harshit, Greg, Sasha, Dave,
Thanks for catching this. You're right, patch #2 of the original series:
https://lore.kernel.org/r/[email protected]/
is missing here.
I've adapted patch #2 ("net: ntb_netdev: Fix TX busy and drop handling") for linux-6.12.y. (Note: only build-tested.)
Would you prefer me to send the full four-patch series for 6.12.y?
Best regards,
Koichiro
--------8<--------
From b64974a682c800ebbac4d1935541b933fcca593f Mon Sep 17 00:00:00 2001
From: Koichiro Den <[email protected]>
Date: Mon, 7 Sep 2026 09:58:58 +0900
Subject: [PATCH] net: ntb_netdev: Fix TX busy and drop handling
commit 8aaa47351db0f93a5c5297fbafdfa8bc75e8ae49 upstream.
Currently, ntb_netdev returns NETDEV_TX_BUSY for every enqueue error. It also increments the drop and error counters while leaving the skb owned by the qdisc, and may return BUSY with the subqueue still awake. Retrying a permanent error cannot succeed either.
The unconditional BUSY return and premature accounting date back to the initial driver. The error-path queue stop was later removed without changing that return value. The current flow-control code includes a resource check, but ntb_netdev does not honor its result before enqueue.
Honor the resource check before enqueue. For -EAGAIN and -EBUSY, stop the subqueue, arm the existing reaper timer, and return BUSY without touching the skb. For other errors, free the skb, increment tx_dropped, and return NETDEV_TX_OK.
[den: adapt to the single-queue implementation, as 6.12.y lacks commit:
ee970634c777 ("net: ntb_netdev: Introduce per-queue context")]
--- drivers/net/ntb_netdev.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-)
drivers/net/ntb_netdev.c +19 -8
@@ -167,8 +167,10 @@ static int __ntb_netdev_maybe_stop_tx(struct net_device *netdev,static int ntb_netdev_maybe_stop_tx (struct net_device *ndev,struct ntb_transport_qp *qp,int size) { -if (netif_queue_stopped (ndev) || - (ntb_transport_tx_free_entry (qp) >= size)) +if (netif_queue_stopped (ndev)) +return -EBUSY ; + +if (ntb_transport_tx_free_entry (qp) >= size)return 0 ;return __ntb_netdev_maybe_stop_tx (ndev, qp, size);
@@ -211,21 +213,30 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,struct ntb_netdev *dev =netdev_priv (ndev);int rc; -ntb_netdev_maybe_stop_tx (ndev, dev->qp, tx_stop); +if (unlikely (ntb_netdev_maybe_stop_tx (ndev, dev->qp, tx_stop))) +return NETDEV_TX_BUSY ; rc =ntb_transport_tx_enqueue (dev->qp, skb, skb->data, skb->len); -if (rc) - goto err; +if (rc) { +if (rc == -EAGAIN || rc == -EBUSY ) { +netif_stop_queue (ndev); +mod_timer (&dev->tx_timer, + jiffies +usecs_to_jiffies (tx_time)); +return NETDEV_TX_BUSY ; + } + + goto drop; + }/* check for next submit */ ntb_netdev_maybe_stop_tx (ndev, dev->qp, tx_stop);return NETDEV_TX_OK ; -err: +drop: +dev_kfree_skb_any (skb); ndev->stats.tx_dropped++; - ndev->stats.tx_errors++; -return NETDEV_TX_BUSY ; +return NETDEV_TX_OK ; }static void ntb_netdev_tx_timer (struct timer_list *t)
Signature · 3 lines
2.51.0 --------8<--------
Quoted 11 lines
Thoughts?
thanks,
HarshitQuoted 5 lines
drivers/ntb/ntb_transport.c +0
-0entry =ntb_list_rm (&qp->ntb_tx_free_q_lock, &qp->tx_free_q);if (!entry) {
Sasha Levin
maintainerRe: [PATCH 6.12 348/403] NTB: ntb_transport: Fail TX enqueue when the QP link is down
In reply to Harshit MogalapalliQuoted 5 lines
I think 6.12.y misses commit:
8aaa47351db0("net:ntb_netdev: Fix TX busy and drop handling"). So we should either pull in the prerequisite or drop this.Thoughts?
Same gap exists on 6.6, 6.1, 5.15 and 5.10 too, not just 6.12 - thanks for flagging it.
A backport of that commit would be appreciated!
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 349/403 NTB: ntb_transport: Reject oversized TX buffers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit a4f2387db6f1cc2f03abba7f3a6807ad61e26ff7 upstream.
ntb_process_tx() handles an oversized buffer by calling tx_handler() with a NULL data pointer and returning success. ntb_netdev therefore neither frees the skb in its completion callback nor takes its enqueue error path, leaking it.
Reject oversized buffers in ntb_transport_tx_enqueue() before acquiring a queue entry and return -EMSGSIZE. The caller retains ownership of the buffer, and the preceding netdev patch frees the skb when enqueue returns this permanent error.
--- drivers/ntb/ntb_transport.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-)
drivers/ntb/ntb_transport.c +3 -9
@@ -1927,15 +1927,6 @@ static int ntb_process_tx(struct ntb_trareturn -EAGAIN ; } -if (entry->len > qp->tx_max_frame -sizeof (struct ntb_payload_header )) { -if (qp->tx_handler) - qp->tx_handler (qp, qp->cb_data,NULL , -EIO ); - -ntb_list_add (&qp->ntb_tx_free_q_lock, &entry->entry, - &qp->tx_free_q); -return 0 ; - } -ntb_async_tx (qp, entry); qp->tx_index++;
@@ -2310,6 +2301,9 @@ int ntb_transport_tx_enqueue(struct ntb_if (!qp->link_is_up)return -ENOLINK ; +if (len > qp->tx_max_frame -sizeof (struct ntb_payload_header )) +return -EMSGSIZE ; + entry =ntb_list_rm (&qp->ntb_tx_free_q_lock, &qp->tx_free_q);if (!entry) { qp->tx_err_no_buf++;
Re: [PATCH 6.12 349/403] NTB: ntb_transport: Reject oversized TX buffers
Hi Greg/Sasha,
Quoted 47 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit
a4f2387db6f1cc2f03abba7f3a6807ad61e26ff7upstream.
ntb_process_tx()handles an oversized buffer by callingtx_handler()with a NULL data pointer and returning success.ntb_netdevtherefore neither frees the skb in its completion callback nor takes its enqueue error path, leaking it.Reject oversized buffers in
ntb_transport_tx_enqueue()before acquiring a queue entry and return-EMSGSIZE. The caller retains ownership of the buffer, and the preceding netdev patch frees the skb when enqueue returns this permanent error.Fixes fce8a7bb5b4b ("PCI-Express Non-Transparent Bridge Support")Signed-off-by Koichiro Den <[email protected]>Reviewed-by Dave Jiang <[email protected]>Signed-off-by Jakub Kicinski <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/ntb/ntb_transport.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-)drivers/ntb/ntb_transport.c +0
-9@@ -1927,15 +1927,6 @@ static int ntb_process_tx(struct ntb_trareturn -EAGAIN ; } -if (entry->len > qp->tx_max_frame -sizeof (struct ntb_payload_header )) { -if (qp->tx_handler) - qp->tx_handler (qp, qp->cb_data,NULL , -EIO ); - -ntb_list_add (&qp->ntb_tx_free_q_lock, &entry->entry, - &qp->tx_free_q); -return 0 ; - } - ntb_async_tx(qp, entry);> qp->tx_index++;
drivers/ntb/ntb_transport.c int ntb_transport_tx_enqueue(struct ntb_ · line 2306
Quoted 6 lines
drivers/ntb/ntb_transport.c +3
-0@@ -2310,6 +2301,9 @@ int ntb_transport_tx_enqueue(struct ntb_if (!qp->link_is_up)return -ENOLINK ; +if (len > qp->tx_max_frame -sizeof (struct ntb_payload_header )) +return -EMSGSIZE ; +
I have run an AI-assisted backport review and it spotted an issue.
Upstream commit: a4f2387db6f1 ("NTB: ntb_transport: Reject oversized TX buffers") returns -EMSGSIZE before dequeuing a transport entry. It relies on preceding commit: 8aaa47351db0 ("net: ntb_netdev: Fix TX busy and drop handling") to free an skb on a permanent enqueue error and return NETDEV_TX_OK.
The 6.12.y ntb_netdev caller still does this for every error:
ndev->stats.tx_dropped++;
ndev->stats.tx_errors++;
return NETDEV_TX_BUSY;It does not free the skb. Thus an oversized packet can never succeed, but commit: 9d457690ff8c ("NTB: ntb_transport: Reject oversized TX buffers") tells the stack to retry it as though the condition were temporary. That can leave the packet stuck and stall NTB transmit.
I think 6.12.y misses commit: 8aaa47351db0 ("net: ntb_netdev: Fix TX busy and drop handling"). Thoughts?
These are all part of patch series:
https://lore.kernel.org/all/[email protected]/
thanks,
Harshit
Quoted 6 lines
drivers/ntb/ntb_transport.c +0
-0entry =ntb_list_rm (&qp->ntb_tx_free_q_lock, &qp->tx_free_q);if (!entry) { qp->tx_err_no_buf++;
Greg Kroah-Hartman
maintainerPATCH 350/403 net: ntb_netdev: Avoid double-accounting netif_rx() drops
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit 82e15be2d8b9efa6fb1750528d9b6f40e6a8eea7 upstream.
netif_rx() already accounts packets it drops in the core rx_dropped counter. ntb_netdev counts them again as both errors and drops.
Leave netif_rx() drops to the core. Count the packet and bytes unconditionally since it was received successfully by the driver.
--- drivers/net/ntb_netdev.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
drivers/net/ntb_netdev.c +3 -7
@@ -125,13 +125,9 @@ static void ntb_netdev_rx_handler(struct skb->protocol =eth_type_trans (skb, ndev); skb->ip_summed =CHECKSUM_NONE ; -if (netif_rx (skb) ==NET_RX_DROP ) { - ndev->stats.rx_errors++; - ndev->stats.rx_dropped++; - }else { - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += len; - } +netif_rx (skb); + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; skb = new_skb;
Greg Kroah-Hartman
maintainerPATCH 351/403 net: ntb_netdev: Count packets dropped on RX refill failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit 31ded341c375bb2faac1d77ab0012a732ba3e2a6 upstream.
When replacement skb allocation fails, ntb_netdev drops a packet that was received successfully and requeues the original buffer. The drop is counted, but rx_packets and rx_bytes are not.
Count every good packet before allocating its replacement.
--- drivers/net/ntb_netdev.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/ntb_netdev.c +3 -2
@@ -115,6 +115,9 @@ static void ntb_netdev_rx_handler(struct goto enqueue_again; } + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + new_skb =netdev_alloc_skb (ndev, ndev->mtu +ETH_HLEN );if (!new_skb) { ndev->stats.rx_dropped++;
@@ -126,8 +129,6 @@ static void ntb_netdev_rx_handler(struct skb->ip_summed =CHECKSUM_NONE ;netif_rx (skb); - ndev->stats.rx_packets++; - ndev->stats.rx_bytes += len; skb = new_skb;
Greg Kroah-Hartman
maintainerPATCH 352/403 net/smc: do not dereference an unset send buffer on the SMC-D teardown path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit b395dd319cea422239cb45b998fb38d7e373af87 upstream.
smc_close_stream_wait() calls smc_tx_prepared_sends() from inside its sk_wait_event() condition, and sk_wait_event() evaluates that condition once with the socket lock released. smcd_buf_detach() clears conn->sndbuf_desc from smc_conn_kill() under lock_sock(), so a link group terminating while a socket waits there leaves the helper dereferencing NULL, faulting out of close(). SIOCOUTQ reads the field by hand, and smc_close_cancel_work() drops the lock across two cancel_*_sync() calls.
Sample the pointer once in the helper, report nothing prepared while it is unset, and bound the ioctl the same way. The receive tasklet dereferences the field directly in smc_cdc_msg_recv_action(), not through this helper; 1/2 is what keeps it from running that late.
--- net/smc/af_smc.c | 3 ++- net/smc/smc_tx.h | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-)
net/smc/af_smc.c +2 -1
@@ -3236,7 +3236,8 @@ int smc_ioctl(struct socket *sock, unsigreturn -EINVAL ; }if (smc->sk.sk_state ==SMC_INIT || - smc->sk.sk_state ==SMC_CLOSED ) + smc->sk.sk_state ==SMC_CLOSED || + !READ_ONCE (smc->conn.sndbuf_desc)) answ =0 ;else answ = smc->conn.sndbuf_desc->len -
net/smc/smc_tx.h +5 -1
@@ -20,11 +20,15 @@static inline int smc_tx_prepared_sends (struct smc_connection *conn) { +struct smc_buf_desc *sndbuf_desc =READ_ONCE (conn->sndbuf_desc);union smc_host_cursor sent, prep; +if (!sndbuf_desc) +return 0 ; +smc_curs_copy (&sent, &conn->tx_curs_sent, conn);smc_curs_copy (&prep, &conn->tx_curs_prep, conn); -return smc_curs_diff (conn->sndbuf_desc->len, &sent, &prep); +return smc_curs_diff (sndbuf_desc->len, &sent, &prep); }void smc_tx_pending (struct smc_connection *conn);
Greg Kroah-Hartman
maintainerPATCH 353/403 net/smc: fix socket refcount leak in smc_switch_conns()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hidayath Khan <[email protected]>
commit 719296c4aa8213d4ac8002e77d5956d436bc98d0 upstream.
smc_switch_conns() takes a reference on the SMC socket before dropping lgr->conns_lock, so the connection stays alive while the CDC slot is fetched:
sock_hold(&smc->sk);
read_unlock_bh(&lgr->conns_lock);
/* pre-fetch buffer outside of send_lock, might sleep */
rc = smc_cdc_get_free_slot(conn, to_lnk, &wr_buf, NULL, &pend);
if (rc)
goto err_out;The err_out label only drops the wr_tx link reference, so this early exit returns without the matching sock_put(). The second error exit is not affected, because sock_put() has already run by then.
A leaked sk_refcnt means the smc_sock is never destroyed. Its send and receive buffers stay allocated, and for a user socket the reference held on the network namespace is never released, so the netns can no longer be torn down.
smc_cdc_get_free_slot() fails when the target link goes down or when the connection has been killed while the switch is in progress. Both are reachable during the link failover this function implements, so the leak is triggered by the same hardware events that make smc_switch_conns() run in the first place.
Restructure so there is a single sock_put() covering both outcomes, instead of adding a second one to the error path.
--- net/smc/smc_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
net/smc/smc_core.c +7 -7
@@ -1129,13 +1129,13 @@ again:read_unlock_bh (&lgr->conns_lock);/* pre-fetch buffer outside of send_lock, might sleep */ rc =smc_cdc_get_free_slot (conn, to_lnk, &wr_buf,NULL , &pend); -if (rc) - goto err_out; -/* avoid race with smcr_tx_sndbuf_nonempty() */ -spin_lock_bh (&conn->send_lock); -smc_switch_link_and_count (conn, to_lnk); - rc =smc_switch_cursor (smc, pend, wr_buf); -spin_unlock_bh (&conn->send_lock); +if (!rc) { +/* avoid race with smcr_tx_sndbuf_nonempty() */ +spin_lock_bh (&conn->send_lock); +smc_switch_link_and_count (conn, to_lnk); + rc =smc_switch_cursor (smc, pend, wr_buf); +spin_unlock_bh (&conn->send_lock); + }sock_put (&smc->sk);if (rc) goto err_out;
Greg Kroah-Hartman
maintainerPATCH 354/403 net/smc: fix use-after-free in smc_rx_pipe_buf_release()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hidayath Khan <[email protected]>
commit c924884743e948e25625b7fbf3ee2a9325a204a7 upstream.
smc_rx_splice() hands RMB pages to a pipe and takes a socket reference per entry so the smc_sock stays alive until the reader finishes. The connection does not: a concurrent close runs smc_conn_free(), which releases the receive buffer back to the link group pool.
smc_rx_pipe_buf_release() tests sk_state before taking the socket lock. The state can change between the test and the lock, and smc_rx_update_cons() then dereferences conn->rmb_desc and walks conn->lgr, which smc_conn_free() has already released. On the is_reg_err path smcr_buf_unuse() frees the descriptor outright, so this is a use-after-free.
Take the socket lock first and test conn->freed instead. smc_conn_free() sets that flag before releasing anything, and every caller holds the socket lock. The two paths exclude each other: either the pipe release runs first with everything valid, or it sees the flag and skips the update.
--- net/smc/smc_rx.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
net/smc/smc_rx.c +5 -6
@@ -115,16 +115,15 @@ static void smc_rx_pipe_buf_release(stru struct pipe_buffer *buf) {struct smc_spd_priv *priv = (struct smc_spd_priv *)buf->private; +struct smc_connection *conn = &priv->smc->conn;struct smc_sock *smc = priv->smc; -struct smc_connection *conn;struct sock *sk = &smc->sk; -if (sk->sk_state ==SMC_CLOSED || - sk->sk_state ==SMC_PEERFINCLOSEWAIT || - sk->sk_state ==SMC_APPFINCLOSEWAIT ) - goto out; - conn = &smc->conn;lock_sock (sk); +if (conn->freed) { +release_sock (sk); + goto out; + }smc_rx_update_cons (smc, priv->len);release_sock (sk);if (atomic_sub_and_test (priv->len, &conn->splice_pending))
Greg Kroah-Hartman
maintainerPATCH 355/403 net/smc: unregister the connection before draining the rx tasklet
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 36cdf5d48ca191dcd71c28cadbe0981b1d25318d upstream.
smc_conn_free() calls smc_ism_unset_conn() only while the link group is still on its device list, and never sets conn->killed. smc_lgr_terminate_sched() unlinks the group immediately and defers killing its connections to a work item, so a connection freed in that window keeps its smcd->conn[] slot with both gates in smcd_handle_irq() open, and the device can re-arm the receive tasklet after tasklet_kill() has returned. On the DMB-nocopy path the ghost send buffer is freed right after that drain, so the re-armed tasklet dereferences it.
Unregister unconditionally and drain before the detach at both teardown sites, mirroring rmb_desc, which smc_buf_unuse() releases after the drain. Clear conn->sndbuf_desc before freeing it as well, so a reader that samples the pointer cannot get one that is already freed.
--- net/smc/smc_core.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
net/smc/smc_core.c +7 -6
@@ -1190,14 +1190,16 @@ static void smcd_buf_detach(struct smc_c {struct smcd_dev *smcd = conn->lgr->smcd;u64 peer_token = conn->peer_token; +struct smc_buf_desc *buf_desc;if (!conn->sndbuf_desc)return ;smc_ism_detach_dmb (smcd, peer_token); -kfree (conn->sndbuf_desc); + buf_desc = conn->sndbuf_desc; conn->sndbuf_desc =NULL ; +kfree (buf_desc); } static void smc_buf_unuse(struct smc_connection *conn,
@@ -1249,11 +1251,10 @@ void smc_conn_free(struct smc_connection goto lgr_put;if (lgr->is_smcd) { -if (!list_empty (&lgr->list)) -smc_ism_unset_conn (conn); +smc_ism_unset_conn (conn); +tasklet_kill (&conn->rx_tsklet);if (smc_ism_support_dmb_nocopy (lgr->smcd))smcd_buf_detach (conn); -tasklet_kill (&conn->rx_tsklet); }else {smc_cdc_wait_pend_tx_wr (conn);if (current_work () != &conn->abort_work)
@@ -1506,12 +1507,12 @@ static void smc_conn_kill(struct smc_consmc_sk_wake_ups (smc);if (conn->lgr->is_smcd) {smc_ism_unset_conn (conn); -if (smc_ism_support_dmb_nocopy (conn->lgr->smcd)) -smcd_buf_detach (conn);if (soft)tasklet_kill (&conn->rx_tsklet);else tasklet_unlock_wait (&conn->rx_tsklet); +if (smc_ism_support_dmb_nocopy (conn->lgr->smcd)) +smcd_buf_detach (conn); }else {smc_cdc_wait_pend_tx_wr (conn); }
Greg Kroah-Hartman
maintainerPATCH 356/403 net: cap advertised IP tunnel headroom
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit 6b222adeb9340306e2ff97127c76117abb9b3df8 upstream.
IP tunnel devices derive their advertised needed_headroom from lower output devices. A stack of user-created devices can make the derived value larger than the 16-bit skb header offsets can represent. Once IP output reserves it, skb head expansion can wrap those offsets.
The runtime transmit path already caps a growing needed_headroom at 512. Apply the same cap when tunnel configuration publishes needed_headroom derived from a lower output device.
Capping the advertised value is safe: IP tunnel transmit still expands the skb when a packet needs more headroom. A nonsensical stacked configuration can therefore incur an extra reallocation, but it cannot publish an unbounded reservation to upper layers.
--- include/net/ip_tunnels.h | 11 +++++++++-- net/ipv4/ip_tunnel.c | 2 +- net/ipv6/ip6_gre.c | 7 +++++-- net/ipv6/ip6_tunnel.c | 7 +++++-- net/ipv6/sit.c | 2 +- 5 files changed, 21 insertions(+), 8 deletions(-)
include/net/ip_tunnels.h +9 -2
@@ -615,8 +615,7 @@ struct metadata_dst *iptunnel_metadata_rint skb_tunnel_check_pmtu (struct sk_buff *skb,struct dst_entry *encap_dst,int headroom,bool reply); -static inline void ip_tunnel_adj_headroom (struct net_device *dev, -unsigned int headroom) +static inline unsigned int ip_tunnel_limit_headroom (unsigned int headroom) {/* we must cap headroom to some upperlimit, else pskb_expand_head * will overflow header offsets in skb_headers_offset_update().
@@ -626,6 +625,14 @@ static inline void ip_tunnel_adj_headrooif (headroom > max_allowed) headroom = max_allowed; +return headroom; +} + +static inline void ip_tunnel_adj_headroom (struct net_device *dev, +unsigned int headroom) +{ + headroom =ip_tunnel_limit_headroom (headroom); +if (headroom >READ_ONCE (dev->needed_headroom))WRITE_ONCE (dev->needed_headroom, headroom); }
net/ipv4/ip_tunnel.c +1 -1
@@ -316,7 +316,7 @@ static int ip_tunnel_bind_dev(struct net mtu =min (tdev->mtu,IP_MAX_MTU ); } - dev->needed_headroom = t_hlen + hlen; + dev->needed_headroom =ip_tunnel_limit_headroom (t_hlen + hlen); mtu -= t_hlen + (dev->type ==ARPHRD_ETHER ? dev->hard_header_len :0 );if (mtu <IPV4_MIN_MTU )
net/ipv6/ip6_gre.c +5 -2
@@ -1165,8 +1165,11 @@ static void ip6gre_tnl_link_config_routereturn ;if (rt->dst.dev) { - dev->needed_headroom = rt->dst.dev->hard_header_len + - t_hlen; +unsigned int headroom; + + headroom = rt->dst.dev->hard_header_len + t_hlen; + headroom =ip_tunnel_limit_headroom (headroom); + dev->needed_headroom = headroom;if (set_mtu) {int mtu = rt->dst.dev->mtu - t_hlen;
net/ipv6/ip6_tunnel.c +5 -2
@@ -1506,8 +1506,11 @@ static void ip6_tnl_link_config(struct i tdev =__dev_get_by_index (t->net, p->link);if (tdev) { - dev->needed_headroom = tdev->hard_header_len + - tdev->needed_headroom + t_hlen; +unsigned int headroom; + + headroom = tdev->hard_header_len + tdev->needed_headroom; + headroom += t_hlen; + dev->needed_headroom =ip_tunnel_limit_headroom (headroom); mtu = min_t(unsigned int, tdev->mtu, IP6_MAX_MTU); mtu = mtu - t_hlen;
net/ipv6/sit.c +1 -1
@@ -1136,7 +1136,7 @@ static void ipip6_tunnel_bind_dev(structWRITE_ONCE (dev->mtu, mtu); hlen = tdev->hard_header_len + tdev->needed_headroom; } - dev->needed_headroom = t_hlen + hlen; + dev->needed_headroom =ip_tunnel_limit_headroom (t_hlen + hlen); } static void ipip6_tunnel_update(struct ip_tunnel *t,
Greg Kroah-Hartman
maintainerPATCH 357/403 net: fix spurious TX timeout after dev_activate()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
commit 82aeed2400786bd3f79d88cb8b8f42e6127e5923 upstream.
While debugging another issue today, I found out that my TX queue is reported as stopped for 4294907392 ms (49.7 days), on a machine that had been up for four minutes.
bnxt_en 0002:01:00.0 eth0: NETDEV WATCHDOG: CPU: 28: transmit queue 23 timed out 4294907392 ms
4294907392 is not an elapsed time. It is the value of jiffies at that
moment: INITIAL_JIFFIES is 4294667296, which leaves jiffies 59 seconds short of wrapping.
dev_activate() runs transition_one_qdisc() over every TX queue, which resets trans_start to 0, and then stamps only queue 0 through netif_trans_update().
Stamp jiffies instead. A queue stopped across dev_activate() now gets a full watchdog_timeo of grace, and is still reported if it is stopped that long.
--- net/sched/sch_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/sched/sch_generic.c +1 -1
@@ -1242,7 +1242,7 @@ static void transition_one_qdisc(structrcu_assign_pointer (dev_queue->qdisc, new_qdisc);if (need_watchdog_p) { -WRITE_ONCE (dev_queue->trans_start,0 ); +WRITE_ONCE (dev_queue->trans_start, jiffies); *need_watchdog_p =1 ; } }
Greg Kroah-Hartman
maintainerPATCH 358/403 net: skbuff: dont touch shared zerocopy state in skb_tx_error()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Norbert Szetei <[email protected]>
commit f66bdb1cc0fcd227a062378f8be0b5873aa5600a upstream.
skb_tx_error() completes the zerocopy uarg and clears SKBFL_ALL_ZEROCOPY, and skb_zcopy_downgrade_managed() clears SKBFL_MANAGED_FRAG_REFS. Both live in skb_shinfo(), which every clone shares, while the caller only owns the reference it is about to drop. Through a clone it tells the producer its pages are free and drops SKBFL_SHARED_FRAG for an skb that is still in flight.
Open vSwitch reaches this with a non-last OVS_ACTION_ATTR_RECIRC:clone_execute() sends a skb_clone() into ovs_dp_process_packet() while do_execute_actions() keeps forwarding the original, and skb_clone() does not privatise the frags here -- skb_orphan_frags() returns early on SKBFL_DONT_ORPHAN. A flow miss on the clone then strips the marker from the packet still being forwarded, and a later local ESP delivery decrypts in place over frags it does not own privately.
Skip it for a cloned skb. Nothing is lost: skb_release_data() clears the zerocopy state once the last reference to the shared data goes.
--- net/core/skbuff.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/core/skbuff.c +4 -1
@@ -1403,10 +1403,13 @@ EXPORT_SYMBOL(skb_dump);* * Report xmit error if a device callback is tracking this skb. * skb must be freed afterwards. +* +* Does nothing for a cloned skb: the zerocopy state lives in +* skb_shinfo(), which the clones share. */ void skb_tx_error (struct sk_buff *skb) { -if (skb) { +if (skb && !skb_cloned (skb)) {skb_zcopy_downgrade_managed (skb);skb_zcopy_clear (skb, true); }
Greg Kroah-Hartman
maintainerPATCH 359/403 seg6: reset IP6CB after IPv6 decapsulation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
commit f967455fb2a5a2079b9eb5823e9ccf359174bf9f upstream.
decap_and_validate() pulls the outer SRv6 headers and makes the inner packet the skb network header. The IPv6 control block still contains values collected while parsing the outer packet, including nhoff and extension-header flags.
End.DX6 and End.DT6 route the inner IPv6 packet directly to the IPv6 input path. An unprivileged user can reach End.DT6 from a user and net namespace by installing a local SID and injecting an outer packet with Hop-by-Hop and Destination Options headers followed by an SRH and a minimal inner IPv6 packet.
The outer extension headers leave a large nhoff in IP6CB. After decapsulation, ip6_protocol_deliver_rcu() uses that stale offset on the inner packet and reads beyond the skb head. KASAN reports:
BUG: KASAN: slab-out-of-bounds in ip6_protocol_deliver_rcu ip6_protocol_deliver_rcu+0x1118/0x1450 ip6_input_finish+0x11b/0x240 seg6_local_input_core+0xed/0x2e0 lwtunnel_input+0x1e9/0x4e0 ipv6_rthdr_rcv+0x525f/0x6c50 ip6_protocol_deliver_rcu+0xcb7/0x1450
Before clearing IP6CB for an inner IPv6 packet, save its incoming interface index and L3 slave state. Restore both after the clear and set nhoff to the inner IPv6 base-header nexthdr field.
Use IP6CB(skb)->iif rather than skb->skb_iif because VRF processing can replace skb_iif with the L3 master while IP6CB keeps the receiving interface. Preserve IP6SKB_L3SLAVE for the same reason.
--- net/ipv6/seg6_local.c | 9 +++++++++ 1 file changed, 9 insertions(+)
net/ipv6/seg6_local.c +9 -0
@@ -261,6 +261,15 @@ static bool decap_and_validate(struct skmemset (IPCB (skb),0 ,sizeof (*IPCB (skb)));IPCB (skb)->iif = iif; + }else if (proto ==IPPROTO_IPV6 ) { +bool l3slave =ipv6_l3mdev_skb (IP6CB (skb)->flags); +int iif =IP6CB (skb)->iif; + +memset (IP6CB (skb),0 ,sizeof (*IP6CB (skb))); +IP6CB (skb)->iif = iif; +IP6CB (skb)->nhoff = offsetof(struct ipv6hdr , nexthdr); +if (l3slave) +IP6CB (skb)->flags |=IP6SKB_L3SLAVE ; }return true;
Greg Kroah-Hartman
maintainerPATCH 360/403 mfd: sm501: Fix potential memory leaks during remove
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 83feedd9d83c0c5199f98c72df0a6196b4aefb4d upstream.
The memory allocated for struct sm501_devdata in sm501_pci_probe() and sm501_plat_probe() is not freed by the corresponding remove functions sm501_pci_remove() and sm501_plat_remove(). Fix that by adding a call to kfree().
--- drivers/mfd/sm501.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/mfd/sm501.c +2 -0
@@ -1667,6 +1667,7 @@ static void sm501_pci_remove(struct pci_release_mem_region (sm->io_res->start,0x100 );pci_disable_device (dev); +kfree (sm); }static void sm501_plat_remove (struct platform_device *dev)
@@ -1677,6 +1678,7 @@ static void sm501_plat_remove(struct plaiounmap (sm->regs);release_mem_region (sm->io_res->start,0x100 ); +kfree (sm); }static const struct pci_device_id sm501_pci_tbl[] = {
Greg Kroah-Hartman
maintainerPATCH 361/403 ALSA: 6fire: bound the MIDI event length from the device
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit a478893b59e36cfe7d77a76b352f2db55502e879 upstream.
usb6fire_comm_receiver_handler() forwards a MIDI event using a length byte the device supplies, with no bound and no check that the transfer delivered that many bytes:
if (!urb->status) {
if (rt->receiver_buffer[0] == 0x10) /* midi in event */
if (midi_rt)
midi_rt->in_received(midi_rt,
rt->receiver_buffer + 2,
rt->receiver_buffer[1]);
}receiver_buffer is a 64-byte kzalloc() buffer (COMM_RECEIVER_BUFSIZE), so only 62 bytes follow the two-byte header. receiver_buffer[1] is a u8 the device chooses, so a device that answers with 0x10 and a length of 0xFF makes snd_rawmidi_receive() read 255 bytes starting two bytes into a 64-byte object. The bytes past the buffer are handed to userspace through the rawmidi read path.
urb->actual_length is not consulted either, so a short transfer leaves both the type byte and the length byte at their previous values and the handler acts on stale data.
The receiver URB is submitted from usb6fire_comm_init() at probe, so the read happens on plug with no user action; forwarding to userspace also needs a MIDI input substream open, since usb6fire_midi_in_received() only calls snd_rawmidi_receive() when rt->in is set.
KASAN on 7.2.0-rc5 (arm64), single packet from an emulated device:
BUG: KASAN: slab-out-of-bounds in snd_rawmidi_receive Read of size 255 at addr ffff000009f64682 by task bash/183 __asan_memcpy snd_rawmidi_receive usb6fire_midi_in_received [snd_usb_6fire] usb6fire_comm_receiver_handler [snd_usb_6fire] Allocated by task 11: usb6fire_comm_init [snd_usb_6fire] usb6fire_chip_probe [snd_usb_6fire] The buggy address is located 2 bytes inside of allocated 64-byte region [ffff000009f64680, ffff000009f646c0)
Reject the event when the length exceeds the bytes that follow the header, and require the transfer to have delivered the header plus that many bytes. The receiver URB is submitted with a 64-byte transfer_buffer_length, so a genuine device cannot deliver an event longer than those 62 bytes and nothing valid is dropped.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/6fire/comm.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
sound/usb/6fire/comm.c +6 -3
@@ -36,11 +36,14 @@ static void usb6fire_comm_receiver_handlstruct midi_runtime *midi_rt = rt->chip->midi;if (!urb->status) { -if (rt->receiver_buffer[0 ] ==0x10 )/* midi in event */ +u8 len = rt->receiver_buffer[1 ]; + +if (rt->receiver_buffer[0 ] ==0x10 &&/* midi in event */ + len <=COMM_RECEIVER_BUFSIZE -2 && + urb->actual_length >= len +2 )if (midi_rt) midi_rt->in_received (midi_rt, - rt->receiver_buffer +2 , - rt->receiver_buffer[1 ]); + rt->receiver_buffer +2 , len); }if (!rt->chip->shutdown) {
Greg Kroah-Hartman
maintainerPATCH 362/403 ALSA: aloop: Check card index validity at probe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit 819b106a9fd2ef3fd8abf898b9a8e4524eca8f48 upstream.
aloop driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.
Add a sanity check for the card index and warn/correct it if it's a value out of the range.
--- sound/drivers/aloop.c | 6 ++++++ 1 file changed, 6 insertions(+)
sound/drivers/aloop.c +6 -0
@@ -1822,6 +1822,12 @@ static int loopback_probe(struct platforint dev = devptr->id;int err; +if (dev <0 || dev >=SNDRV_CARDS ) { +dev_warn (&devptr->dev, +"Invalid card index %d, using default 0\n" , dev); + dev =0 ; + } + err =snd_devm_card_new (&devptr->dev, index[dev], id[dev],THIS_MODULE ,sizeof (struct loopback ), &card);if (err <0 )
Greg Kroah-Hartman
maintainerPATCH 363/403 ALSA: bcd2000: clear the URB pointers on disconnect
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baul Lee <[email protected]>
commit 459d3a64766f5ca2f1886daeaf24582831a5f5ab upstream.
bcd2000_free_usb_related_resources() frees both URBs and leaves the pointers behind:
usb_kill_urb(bcd2k->midi_out_urb); usb_kill_urb(bcd2k->midi_in_urb); usb_free_urb(bcd2k->midi_out_urb); usb_free_urb(bcd2k->midi_in_urb);
The rawmidi device outlives that call. A substream that is still open when the device is unplugged reaches bcd2000_midi_send() from the trigger path on close. That function writes to the freed URB and then hands it to the USB core:
bcd2k->midi_out_urb->transfer_buffer_length = BUFSIZE; ... ret = usb_submit_urb(bcd2k->midi_out_urb, GFP_ATOMIC);
usb_kill_urb() does not stop a later submission either, so a submit that races the disconnect can requeue the URB after it has been reaped. midi_in_urb is exposed the same way: bcd2000_input_complete() resubmits it from the completion handler.
KASAN on 7.2.0-rc5 (arm64):
BUG: KASAN: slab-use-after-free in bcd2000_midi_send [snd_bcd2000] Write of size 4 at addr ffff00001827d388 by task bpoc/168 __asan_store4 bcd2000_midi_send [snd_bcd2000] bcd2000_midi_output_trigger [snd_bcd2000] snd_rawmidi_kernel_write1 close_substream.part.0 Freed by task 168: usb_free_urb bcd2000_disconnect [snd_bcd2000] BUG: KASAN: slab-use-after-free in usb_submit_urb Read of size 8 at addr ffff00001827d3b8 by task bpoc/168
Clear both pointers after freeing and test them on the paths that can still run. Poison the URBs before freeing them: usb_poison_urb() waits for a running completion handler and rejects any later submission, so after it returns the input path is quiesced and only the rawmidi trigger path can still reach bcd2000_midi_send(). No unpoison is needed; the URBs are freed on the next line.
Discovered by XBOW, triaged by Baul Lee <[email protected]>
--- sound/usb/bcd2000/bcd2000.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
sound/usb/bcd2000/bcd2000.c +8 -3
@@ -134,6 +134,9 @@ static void bcd2000_midi_send(struct bcdif (!midi_out_substream)return ; +if (!bcd2k->midi_out_urb) +return ; +/* copy command prefix bytes */ memcpy (bcd2k->midi_out_buf, device_cmd_prefix,sizeof (device_cmd_prefix));
@@ -178,7 +181,7 @@ static int bcd2000_midi_output_close(str {struct bcd2000 *bcd2k = substream->rmidi->private_data; -if (bcd2k->midi_out_active) { +if (bcd2k->midi_out_active && bcd2k->midi_out_urb) {usb_kill_urb (bcd2k->midi_out_urb); bcd2k->midi_out_active =0 ; }
@@ -348,11 +351,13 @@ static int bcd2000_init_midi(struct bcd2static void bcd2000_free_usb_related_resources (struct bcd2000 *bcd2k,struct usb_interface *interface) { -usb_kill_urb (bcd2k->midi_out_urb); -usb_kill_urb (bcd2k->midi_in_urb); +usb_poison_urb (bcd2k->midi_out_urb); +usb_poison_urb (bcd2k->midi_in_urb);usb_free_urb (bcd2k->midi_out_urb);usb_free_urb (bcd2k->midi_in_urb); + bcd2k->midi_out_urb =NULL ; + bcd2k->midi_in_urb =NULL ;if (bcd2k->intf) {usb_set_intfdata (bcd2k->intf,NULL );
Greg Kroah-Hartman
maintainerPATCH 364/403 ALSA: mpu401: Check card index validity at probe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit f7dcecb92ed192ff5fcf842918fb1aaea84b5bdd upstream.
mpu401 driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.
Add a sanity check for the card index and warn/correct it if it's a value out of the range.
--- sound/drivers/mpu401/mpu401.c | 6 ++++++ 1 file changed, 6 insertions(+)
sound/drivers/mpu401/mpu401.c +6 -0
@@ -89,6 +89,12 @@ static int snd_mpu401_probe(struct platfint err;struct snd_card *card; +if (dev <0 || dev >=SNDRV_CARDS ) { +dev_warn (&devptr->dev, +"Invalid card index %d, using default 0\n" , dev); + dev =0 ; + } +if (port[dev] ==SNDRV_AUTO_PORT ) {dev_err (&devptr->dev,"specify port\n" );return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 365/403 ALSA: mts64: Check card index validity at probe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit d18a260720f86a5f8b5fcfefc4ba2e9dd01c10f8 upstream.
Although mts64 driver has a check of the given devptr->id value, it doesn't check for a negative id, which is often given as "none" or such value when bound via sysfs. This may lead to OOB access for index[] and other parameters.
Add a sanity check for the card index and warn/correct it if it's a value out of the range.
--- sound/drivers/mts64.c | 6 ++++++ 1 file changed, 6 insertions(+)
sound/drivers/mts64.c +6 -0
@@ -918,6 +918,12 @@ static int snd_mts64_probe(struct platfo p =platform_get_drvdata (pdev);platform_set_drvdata (pdev,NULL ); +if (dev <0 ) { +dev_warn (&pdev->dev, +"Invalid card index %d, using default 0\n" , dev); + dev =0 ; + } +if (dev >=SNDRV_CARDS )return -ENODEV ;if (!enable[dev])
Greg Kroah-Hartman
maintainerPATCH 366/403 ALSA: pcxhr: initialize mutexes before requesting threaded IRQ
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit 6c97817e20598e5473094e0e38d1f51f1cf4dfff upstream.
pcxhr_probe() requests pcxhr_threaded_irq() before initializing mgr->lock, even though the threaded handler takes that mutex.
Initialize the manager locks before request_threaded_irq() so an early interrupt cannot run against uninitialized mutex state during probe.
--- sound/pci/pcxhr/pcxhr.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
sound/pci/pcxhr/pcxhr.c +7 -7
@@ -1551,6 +1551,13 @@ static int pcxhr_probe(struct pci_dev *p mgr->pci = pci; mgr->irq =-1 ; +/* ISR lock */ +mutex_init (&mgr->lock); +mutex_init (&mgr->msg_lock); + +/* init setup mutex*/ +mutex_init (&mgr->setup_mutex); +if (request_threaded_irq (pci->irq, pcxhr_interrupt, pcxhr_threaded_irq,IRQF_SHARED ,KBUILD_MODNAME , mgr)) {
@@ -1564,13 +1571,6 @@ static int pcxhr_probe(struct pci_dev *p"Digigram at 0x%lx & 0x%lx, 0x%lx irq %i" , mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq); -/* ISR lock */ -mutex_init (&mgr->lock); -mutex_init (&mgr->msg_lock); - -/* init setup mutex*/ -mutex_init (&mgr->setup_mutex); - mgr->prmh = kmalloc(sizeof(*mgr->prmh) + sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS - PCXHR_SIZE_MAX_STATUS),
Greg Kroah-Hartman
maintainerPATCH 367/403 ALSA: portman2x4: Check card index validity at probe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit 3690ef20469d5959378260e2752f2314a2572913 upstream.
Although portman2x4 driver has a check of the given devptr->id value, it doesn't check for a negative id, which is often given as "none" or such value when bound via sysfs. This may lead to OOB access for index[] and other parameters.
Add a sanity check for the card index and warn/correct it if it's a value out of the range.
--- sound/drivers/portman2x4.c | 6 ++++++ 1 file changed, 6 insertions(+)
sound/drivers/portman2x4.c +6 -0
@@ -703,6 +703,12 @@ static int snd_portman_probe(struct plat p =platform_get_drvdata (pdev);platform_set_drvdata (pdev,NULL ); +if (dev <0 ) { +dev_warn (&pdev->dev, +"Invalid card index %d, using default 0\n" , dev); + dev =0 ; + } +if (dev >=SNDRV_CARDS )return -ENODEV ;if (!enable[dev])
Greg Kroah-Hartman
maintainerPATCH 368/403 ALSA: serial-u16550: Check card index validity at probe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit e0fb960b227fcdebe22e4f26c9486d60943c0424 upstream.
serial-u16550 driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.
Add a sanity check for the card index and warn/correct it if it's a value out of the range.
--- sound/drivers/serial-u16550.c | 6 ++++++ 1 file changed, 6 insertions(+)
sound/drivers/serial-u16550.c +6 -0
@@ -864,6 +864,12 @@ static int snd_serial_probe(struct platfint err;int dev = devptr->id; +if (dev <0 || dev >=SNDRV_CARDS ) { +dev_warn (&devptr->dev, +"Invalid card index %d, using default 0\n" , dev); + dev =0 ; + } +switch (adaptor[dev]) {case SNDRV_SERIAL_SOUNDCANVAS : ins[dev] =1 ;
Greg Kroah-Hartman
maintainerPATCH 369/403 ALSA: virmidi: Check card index validity at probe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit b65d5182ecd6b7a24a83d980a0d06e809ef876c5 upstream.
virmidi driver blindly trusts that the given devptr->id value is within the proper card index range at probe. It's OK for the devices the driver itself creates at the module probe time, but if the device is bound manually via sysfs interface, this could be -1 as "none", and this leads to OOB access for index[] and other parameters.
Add a sanity check for the card index and warn/correct it if it's a value out of the range.
--- sound/drivers/virmidi.c | 6 ++++++ 1 file changed, 6 insertions(+)
sound/drivers/virmidi.c +6 -0
@@ -75,6 +75,12 @@ static int snd_virmidi_probe(struct platint idx, err;int dev = devptr->id; +if (dev <0 || dev >=SNDRV_CARDS ) { +dev_warn (&devptr->dev, +"Invalid card index %d, using default 0\n" , dev); + dev =0 ; + } + err =snd_devm_card_new (&devptr->dev, index[dev], id[dev],THIS_MODULE ,sizeof (struct snd_card_virmidi ), &card);if (err <0 )
Greg Kroah-Hartman
maintainerPATCH 370/403 ring-buffer: Fix subbuf resize race with ring buffer readers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
[ Upstream commit 8a5f63637890f03177146efddaba5ec7a1b4d61f ]
trace_buffer subbuf_size is read lockless in ring_buffer_read_page() and ring_buffer_read_start(), while it can simultaneously be resized with ring_buffer_subbuf_order_set().
Instead of trace_buffer::subbuf_size, use bpage::order in ring_buffer_read_start() and ring_buffer_read_page().
In ring_buffer_read_start(), even with resize_disabled, there is still a possibility of a race with a buffer modification. Hold the trace_buffer mutex to synchronise with any pending ring buffer order modification.
trace_buffer::subbuf_size is now actually useless, remove it. Also, create accessors rb_subbuf_capacity() and rb_page_capacity() which return the actual size available for storing events, while rb_subbuf_size() returns the actual subbuf page-size.
--- kernel/trace/ring_buffer.c | 162 ++++++++++++++++++++++++------------- 1 file changed, 104 insertions(+), 58 deletions(-)
kernel/trace/ring_buffer.c +104 -58
@@ -392,6 +392,17 @@ static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)return local_read (&bpage->page->commit); } +/** +* rb_page_capacity - Get the capacity of a buffer page +* @bpage: The buffer page +* +* Return: The maximum size available for events in the given buffer page. +*/ +static __always_inline unsigned int rb_page_capacity(struct buffer_page *bpage) +{ +return (PAGE_SIZE << bpage->order) -BUF_PAGE_HDR_SIZE ; +} +static void free_buffer_page (struct buffer_page *bpage) {/* Range pages are not to be freed */
@@ -557,11 +568,42 @@ struct trace_buffer {long last_text_delta;long last_data_delta; -unsigned int subbuf_size;unsigned int subbuf_order;unsigned int max_data_size; }; +static __always_inline unsigned int rb_subbuf_size(struct trace_buffer *buffer) +{ +return PAGE_SIZE << buffer->subbuf_order; +} + +/** +* rb_subbuf_capacity - Get the capacity of a subbuffer +* @buffer: A trace buffer +* +* Unsafe to use without holding trace_buffer::mutex or with resizing enabled. +* Consider rb_page_capacity() instead. +* +* Return: The maximum size available for events in a trace buffer subbuffer. +*/ +static __always_inline unsigned int rb_subbuf_capacity(struct trace_buffer *buffer) +{ +return rb_subbuf_size (buffer) -BUF_PAGE_HDR_SIZE ; +} + +/** +* rb_subbuf_start - Get the start address of a subbuffer +* @buffer: A trace buffer +* @addr: An address of an event on a subbuffer +* +* Return: The start of the subbuffer for where @addr sits +*/ +static __always_inline +unsigned long rb_subbuf_start (struct trace_buffer *buffer,unsigned long addr) +{ +return addr & ~((unsigned long )(rb_subbuf_size (buffer) -1 )); +} +struct ring_buffer_iter {struct ring_buffer_per_cpu *cpu_buffer;unsigned long head;
@@ -601,7 +643,7 @@ int ring_buffer_print_page_header(struct trace_buffer *buffer, struct trace_seqtrace_seq_printf (s,"\tfield: char data;\t" "offset:%u;\tsize:%u;\tsigned:%u;\n" , (unsigned int )offsetof(typeof (field ), data), - (unsigned int )buffer->subbuf_size, + (unsigned int )rb_subbuf_capacity (buffer), (unsigned int )is_signed_type (char));return !trace_seq_has_overflowed (s);
@@ -1583,7 +1625,7 @@ rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs)*/ static void *rb_range_meta (struct trace_buffer *buffer,int nr_pages,int cpu) { -int subbuf_size = buffer->subbuf_size +BUF_PAGE_HDR_SIZE ; +int subbuf_size =rb_subbuf_size (buffer);unsigned long ptr = buffer->range_addr_start;struct ring_buffer_meta *meta;int nr_subbufs;
@@ -2244,7 +2286,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) }else { page =alloc_pages_node (cpu_to_node (cpu),GFP_KERNEL | __GFP_COMP | __GFP_ZERO, - cpu_buffer->buffer->subbuf_order); + bpage->order);if (!page) goto fail_free_reader; bpage->page =page_address (page);
@@ -2354,10 +2396,9 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, buffer->subbuf_order = order; subbuf_size = (PAGE_SIZE << order); - buffer->subbuf_size = subbuf_size -BUF_PAGE_HDR_SIZE ;/* Max payload is buffer page size - header (8bytes) */ - buffer->max_data_size = buffer->subbuf_size - (sizeof (u32) *2 ); + buffer->max_data_size =rb_subbuf_capacity (buffer) - (sizeof (u32) *2 ); buffer->flags = flags; buffer->clock = trace_clock_local;
@@ -2417,9 +2458,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,rb_range_meta_init (buffer, nr_pages); }else { -/* need at least two pages */ - nr_pages =DIV_ROUND_UP (size, buffer->subbuf_size); + nr_pages =DIV_ROUND_UP (size,rb_subbuf_capacity (buffer));if (nr_pages <2 ) nr_pages =2 ; }
@@ -2794,7 +2834,7 @@ static void update_pages_handler(struct work_struct *work)* @size: the new size. * @cpu_id: the cpu buffer to resize * -* Minimum size is 2 * buffer->subbuf_size. +* Minimum size is 2 * rb_subbuf_capacity(buffer). * * Returns 0 on success and < 0 on failure. */
@@ -2816,12 +2856,6 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size, !cpumask_test_cpu(cpu_id, buffer->cpumask))return 0 ; - nr_pages =DIV_ROUND_UP (size, buffer->subbuf_size); - -/* we need a minimum of two pages */ -if (nr_pages <2 ) - nr_pages =2 ; -/* * Keep CPUs from coming online while resizing to synchronize * with new per CPU buffers being created.
@@ -2832,6 +2866,12 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size,mutex_lock (&buffer->mutex);atomic_inc (&buffer->resizing); + nr_pages =DIV_ROUND_UP (size,rb_subbuf_capacity (buffer)); + +/* we need a minimum of two pages */ +if (nr_pages <2 ) + nr_pages =2 ; +if (cpu_id ==RING_BUFFER_ALL_CPUS ) {/* * Don't succeed if resizing is disabled, as a reader might be
@@ -3110,7 +3150,7 @@ rb_event_index(struct ring_buffer_per_cpu *cpu_buffer, struct ring_buffer_event {unsigned long addr = (unsigned long )event; - addr &= (PAGE_SIZE << cpu_buffer->buffer->subbuf_order) -1 ; + addr &= (unsigned long )rb_subbuf_size (cpu_buffer->buffer) -1 ;return addr -BUF_PAGE_HDR_SIZE ; }
@@ -3349,8 +3389,8 @@ static inline void rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer, unsigned long tail, struct rb_event_info *info) { - unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size); struct buffer_page *tail_page = info->tail_page; +unsigned long bsize =rb_page_capacity (tail_page);struct ring_buffer_event *event;unsigned long length = info->length;
@@ -3702,8 +3742,7 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, new_index =rb_event_index (cpu_buffer, event); old_index = new_index +rb_event_ts_length (event); - addr = (unsigned long )event; - addr &= ~((PAGE_SIZE << cpu_buffer->buffer->subbuf_order) -1 ); + addr =rb_subbuf_start (cpu_buffer->buffer, (unsigned long )event); bpage =READ_ONCE (cpu_buffer->tail_page);
@@ -4350,7 +4389,7 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer, tail = write - info->length;/* See if we shot pass the end of this buffer page */ -if (unlikely (write > cpu_buffer->buffer->subbuf_size)) { +if (unlikely (write >rb_page_capacity (tail_page))) {check_buffer (cpu_buffer, info,CHECK_FULL_PAGE );return rb_move_tail (cpu_buffer, tail, info); }
@@ -4595,7 +4634,7 @@ rb_decrement_entry(struct ring_buffer_per_cpu *cpu_buffer,struct buffer_page *bpage = cpu_buffer->commit_page;struct buffer_page *start; - addr &= ~((PAGE_SIZE << cpu_buffer->buffer->subbuf_order) -1 ); + addr =rb_subbuf_start (cpu_buffer->buffer, addr);/* Do the likely case first */ if (likely (bpage->page == (void *)addr)) {
@@ -5292,7 +5331,6 @@ static struct buffer_page * rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) {struct buffer_page *reader =NULL ; -unsigned long bsize =READ_ONCE (cpu_buffer->buffer->subbuf_size);unsigned long overwrite;unsigned long flags;int nr_loops =0 ;
@@ -5432,7 +5470,7 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)#define USECS_WAIT 1000000for (nr_loops =0 ; nr_loops <USECS_WAIT ; nr_loops++) {/* If the write is past the end of page, a writer is still updating it */ -if (likely (!reader ||rb_page_write (reader) <= bsize)) +if (likely (!reader ||rb_page_write (reader) <=rb_page_capacity (reader)))break ;udelay (1 );
@@ -5854,36 +5892,44 @@ EXPORT_SYMBOL_GPL(ring_buffer_consume);struct ring_buffer_iter *ring_buffer_read_start (struct trace_buffer *buffer,int cpu,gfp_t flags) { + struct ring_buffer_iter *iter __free(kfree) = kzalloc_obj(*iter, flags);struct ring_buffer_per_cpu *cpu_buffer; -struct ring_buffer_iter *iter; - -if (!cpumask_test_cpu (cpu, buffer->cpumask)) -return NULL ; - iter =kzalloc (sizeof (*iter), flags);if (!iter)return NULL ; -/* Holds the entire event: data and meta data */ - iter->event_size = buffer->subbuf_size; - iter->event =kmalloc (iter->event_size, flags); -if (!iter->event) { -kfree (iter); +if (!cpumask_test_cpu (cpu, buffer->cpumask))return NULL ; - } cpu_buffer = buffer->buffers[cpu]; - iter->cpu_buffer = cpu_buffer; +/* +* Only KDB is using GFP_ATOMIC, for the others, lock the buffer to +* prevent concurrent resizing. +*/ +if (gfpflags_allow_blocking (flags)) +mutex_lock (&buffer->mutex);atomic_inc (&cpu_buffer->resize_disabled); +if (gfpflags_allow_blocking (flags)) +mutex_unlock (&buffer->mutex); + +/* Holds the entire event: data and meta data. */ + iter->event_size =rb_page_capacity (READ_ONCE (cpu_buffer->reader_page)); + iter->event =kmalloc (iter->event_size, flags); +if (!iter->event) { +atomic_dec (&cpu_buffer->resize_disabled); +return NULL ; + } + iter->cpu_buffer = cpu_buffer; +guard (raw_spinlock_irqsave)(&cpu_buffer->reader_lock);arch_spin_lock (&cpu_buffer->lock);rb_iter_reset (iter);arch_spin_unlock (&cpu_buffer->lock); -return iter; +return_ptr (iter); }EXPORT_SYMBOL_GPL (ring_buffer_read_start);
@@ -5937,7 +5983,7 @@ unsigned long ring_buffer_size(struct trace_buffer *buffer, int cpu)if (!cpumask_test_cpu (cpu, buffer->cpumask))return 0 ; -return buffer->subbuf_size * buffer->buffers[cpu]->nr_pages; +return rb_subbuf_capacity (buffer) * buffer->buffers[cpu]->nr_pages; }EXPORT_SYMBOL_GPL (ring_buffer_size);
@@ -6496,15 +6542,15 @@ int ring_buffer_read_page(struct trace_buffer *buffer,if (!data_page || !data_page->data)return -1 ; -if (data_page->order != buffer->subbuf_order) -return -1 ; - bpage = data_page->data;if (!bpage)return -1 ;guard (raw_spinlock_irqsave)(&cpu_buffer->reader_lock); +if (data_page->order != cpu_buffer->reader_page->order) +return -1 ; + reader =rb_get_reader_page (cpu_buffer);if (!reader)return -1 ;
@@ -6619,7 +6665,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,/* If there is room at the end of the page to save the * missed events, then record it there. */ -if (buffer->subbuf_size - commit >=sizeof (missed_events)) { +if (rb_page_capacity (reader) - commit >=sizeof (missed_events)) {memcpy (&bpage->data[commit], &missed_events,sizeof (missed_events));local_add (RB_MISSED_STORED , &bpage->commit);
@@ -6631,8 +6677,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,/* * This page may be off to user land. Zero it out here. */ -if (commit < buffer->subbuf_size) -memset (&bpage->data[commit],0 , buffer->subbuf_size - commit); +if (commit <rb_page_capacity (reader)) +memset (&bpage->data[commit],0 ,rb_page_capacity (reader) - commit);return read; }
@@ -6658,7 +6704,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_read_page_data);*/ int ring_buffer_subbuf_size_get (struct trace_buffer *buffer) { -return buffer->subbuf_size +BUF_PAGE_HDR_SIZE ; +return rb_subbuf_size (buffer); }EXPORT_SYMBOL_GPL (ring_buffer_subbuf_size_get);
@@ -6703,7 +6749,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) {struct ring_buffer_per_cpu *cpu_buffer;struct buffer_page *bpage, *tmp; -int old_order, old_size; +unsigned int old_capacity; +int old_order;int nr_pages;int psize;int err;
@@ -6712,9 +6759,6 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)if (!buffer || order <0 )return -EINVAL ; -if (buffer->subbuf_order == order) -return 0 ; - psize = (1 << order) *PAGE_SIZE ;if (psize <=BUF_PAGE_HDR_SIZE )return -EINVAL ;
@@ -6723,18 +6767,21 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order)if (psize >RB_WRITE_MASK +1 )return -EINVAL ; - old_order = buffer->subbuf_order; - old_size = buffer->subbuf_size; -/* prevent another thread from changing buffer sizes */ guard (mutex)(&buffer->mutex); + + old_order = buffer->subbuf_order; +if (old_order == order) +return 0 ; + + old_capacity =rb_subbuf_capacity (buffer); +atomic_inc (&buffer->record_disabled);/* Make sure all commits have finished */ synchronize_rcu (); buffer->subbuf_order = order; - buffer->subbuf_size = psize -BUF_PAGE_HDR_SIZE ;/* Make sure all new buffers are allocated, before deleting the old ones */ for_each_buffer_cpu (buffer, cpu) {
@@ -6750,8 +6797,8 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) }/* Update the number of pages to match the new size */ - nr_pages = old_size * buffer->buffers[cpu]->nr_pages; - nr_pages =DIV_ROUND_UP (nr_pages, buffer->subbuf_size); + nr_pages = old_capacity * buffer->buffers[cpu]->nr_pages; + nr_pages =DIV_ROUND_UP (nr_pages,rb_subbuf_capacity (buffer));/* we need a minimum of two pages */ if (nr_pages <2 )
@@ -6839,7 +6886,6 @@ int ring_buffer_subbuf_order_set(struct trace_buffer *buffer, int order) error: buffer->subbuf_order = old_order; - buffer->subbuf_size = old_size;atomic_dec (&buffer->record_disabled);
@@ -6911,7 +6957,7 @@ static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer, meta->meta_struct_len =sizeof (*meta); meta->nr_subbufs = nr_subbufs; - meta->subbuf_size = cpu_buffer->buffer->subbuf_size +BUF_PAGE_HDR_SIZE ; + meta->subbuf_size =rb_subbuf_size (cpu_buffer->buffer); meta->meta_page_size = meta->subbuf_size;rb_update_meta_page (cpu_buffer);
@@ -7270,7 +7316,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu)* missed events, then record it there. */ commit =rb_page_size (reader); -if (buffer->subbuf_size - commit >=sizeof (missed_events)) { +if (rb_subbuf_capacity (buffer) - commit >=sizeof (missed_events)) {memcpy (&bpage->data[commit], &missed_events,sizeof (missed_events));local_add (RB_MISSED_STORED , &bpage->commit);
@@ -7302,7 +7348,7 @@ int ring_buffer_map_get_reader(struct trace_buffer *buffer, int cpu) out:/* Some archs do not have data cache coherency between kernel and user-space */ flush_kernel_vmap_range (cpu_buffer->reader_page->page, - buffer->subbuf_size +BUF_PAGE_HDR_SIZE ); +rb_subbuf_size (buffer));rb_update_meta_page (cpu_buffer);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 371/403 iommu/amd: remove return value of amd_iommu_detect
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gao Shiyuan <[email protected]>
[ Upstream commit 5bb494d5cbb9a3403ba8b1c8bc145b42fc119078 ]
The return value of amd_iommu_detect is not used, so remove it and is consistent with other iommu detect functions.
--- drivers/iommu/amd/init.c | 10 ++++------ include/linux/amd-iommu.h | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-)
drivers/iommu/amd/init.c +4 -6
@@ -3484,25 +3484,23 @@ static bool amd_iommu_sme_check(void)* IOMMUs * ****************************************************************************/ -int __init amd_iommu_detect (void ) +void __init amd_iommu_detect (void ) {int ret;if (no_iommu || (iommu_detected && !gart_iommu_aperture)) -return -ENODEV ; +return ;if (!amd_iommu_sme_check ()) -return -ENODEV ; +return ; ret =iommu_go_to_state (IOMMU_IVRS_DETECTED );if (ret) -return ret; +return ; amd_iommu_detected = true; iommu_detected =1 ; x86_init.iommu.iommu_init = amd_iommu_init; - -return 1 ; }/****************************************************************************
include/linux/amd-iommu.h +2 -2
@@ -31,11 +31,11 @@ struct amd_iommu_pi_data {struct task_struct ;struct pci_dev ; -extern int amd_iommu_detect (void ); +extern void amd_iommu_detect (void );#else /* CONFIG_AMD_IOMMU */ -static inline int amd_iommu_detect (void ) {return -ENODEV ; } +static inline void amd_iommu_detect (void ) { }#endif /* CONFIG_AMD_IOMMU */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 372/403 x86/sev: Fix broken SNP support with KVM module built-in
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ashish Kalra <[email protected]>
[ Upstream commit 409f45387c937145adeeeebc6d6032c2ec232b35 ]
Fix issues with enabling SNP host support and effectively SNP support which is broken with respect to the KVM module being built-in.
SNP host support is enabled in snp_rmptable_init() which is invoked as device_initcall(). SNP check on IOMMU is done during IOMMU PCI init (IOMMU_PCI_INIT stage). And for that reason snp_rmptable_init() is currently invoked via device_initcall() and cannot be invoked via subsys_initcall() as core IOMMU subsystem gets initialized via subsys_initcall().
Now, if kvm_amd module is built-in, it gets initialized before SNP host support is enabled in snp_rmptable_init() :
[ 10.131811] kvm_amd: TSC scaling supported [ 10.136384] kvm_amd: Nested Virtualization enabled [ 10.141734] kvm_amd: Nested Paging enabled [ 10.146304] kvm_amd: LBR virtualization supported [ 10.151557] kvm_amd: SEV enabled (ASIDs 100 - 509) [ 10.156905] kvm_amd: SEV-ES enabled (ASIDs 1 - 99) [ 10.162256] kvm_amd: SEV-SNP enabled (ASIDs 1 - 99) [ 10.171508] kvm_amd: Virtual VMLOAD VMSAVE supported [ 10.177052] kvm_amd: Virtual GIF supported ... ... [ 10.201648] kvm_amd: in svm_enable_virtualization_cpu
And then svm_x86_ops->enable_virtualization_cpu() (svm_enable_virtualization_cpu) programs MSR_VM_HSAVE_PA as following:wrmsrl(MSR_VM_HSAVE_PA, sd->save_area_pa);
So VM_HSAVE_PA is non-zero before SNP support is enabled on all CPUs.
snp_rmptable_init() gets invoked after svm_enable_virtualization_cpu() as following :
...
[ 11.256138] kvm_amd: in svm_enable_virtualization_cpu ... [ 11.264918] SEV-SNP: in snp_rmptable_init
This triggers a #GP exception in snp_rmptable_init() when snp_enable() is invoked to set SNP_EN in SYSCFG MSR:
[ 11.294289] unchecked MSR access error: WRMSR to 0xc0010010 (tried to write 0x0000000003fc0000) at rIP: 0xffffffffaf5d5c28 (native_write_msr+0x8/0x30) ... [ 11.294404] Call Trace: [ 11.294482] <IRQ> [ 11.294513] ? show_stack_regs+0x26/0x30 [ 11.294522] ? ex_handler_msr+0x10f/0x180 [ 11.294529] ? search_extable+0x2b/0x40 [ 11.294538] ? fixup_exception+0x2dd/0x340 [ 11.294542] ? exc_general_protection+0x14f/0x440 [ 11.294550] ? asm_exc_general_protection+0x2b/0x30 [ 11.294557] ? __pfx_snp_enable+0x10/0x10 [ 11.294567] ? native_write_msr+0x8/0x30 [ 11.294570] ? __snp_enable+0x5d/0x70 [ 11.294575] snp_enable+0x19/0x20 [ 11.294578] __flush_smp_call_function_queue+0x9c/0x3a0 [ 11.294586] generic_smp_call_function_single_interrupt+0x17/0x20 [ 11.294589] __sysvec_call_function+0x20/0x90 [ 11.294596] sysvec_call_function+0x80/0xb0 [ 11.294601] </IRQ> [ 11.294603] <TASK> [ 11.294605] asm_sysvec_call_function+0x1f/0x30 ... [ 11.294631] arch_cpu_idle+0xd/0x20 [ 11.294633] default_idle_call+0x34/0xd0 [ 11.294636] do_idle+0x1f1/0x230 [ 11.294643] ? complete+0x71/0x80 [ 11.294649] cpu_startup_entry+0x30/0x40 [ 11.294652] start_secondary+0x12d/0x160 [ 11.294655] common_startup_64+0x13e/0x141 [ 11.294662] </TASK>
This #GP exception is getting triggered due to the following errata for AMD family 19h Models 10h-1Fh Processors:
Processor may generate spurious #GP(0) Exception on WRMSR instruction:
Description:
The Processor will generate a spurious #GP(0) Exception on a WRMSR instruction if the following conditions are all met:
- the target of the WRMSR is a SYSCFG register.
- the write changes the value of SYSCFG.SNPEn from 0 to 1.
- One of the threads that share the physical core has a non-zero value in the VM_HSAVE_PA MSR.
The document being referred to above:
https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/revision-guides/57095-PUB_1_01.pdf
To summarize, with kvm_amd module being built-in, KVM/SVM initialization happens before host SNP is enabled and this SVM initialization sets VM_HSAVE_PA to non-zero, which then triggers a #GP when SYSCFG.SNPEn is being set and this will subsequently cause SNP_INIT(_EX) to fail with INVALID_CONFIG error as SYSCFG[SnpEn] is not set on all CPUs.
Essentially SNP host enabling code should be invoked before KVM initialization, which is currently not the case when KVM is built-in.
Add fix to call snp_rmptable_init() early from iommu_snp_enable() directly and not invoked via device_initcall() which enables SNP host support before KVM initialization with kvm_amd module built-in.
Add additional handling for iommu=off or amd_iommu=off options.
Note that IOMMUs need to be enabled for SNP initialization, therefore, if host SNP support is enabled but late IOMMU initialization fails then that will cause PSP driver's SNP_INIT to fail as IOMMU SNP sanity checks in SNP firmware will fail with invalid configuration error as below:
[ 9.723114] ccp 0000:23:00.1: sev enabled [ 9.727602] ccp 0000:23:00.1: psp enabled [ 9.732527] ccp 0000:a2:00.1: enabling device (0000 -> 0002) [ 9.739098] ccp 0000:a2:00.1: no command queues available [ 9.745167] ccp 0000:a2:00.1: psp enabled [ 9.805337] ccp 0000:23:00.1: SEV-SNP: failed to INIT rc -5, error 0x3 [ 9.866426] ccp 0000:23:00.1: SEV API:1.53 build:5
Message-ID: <138b520fb83964782303b43ade4369cd181fdd9c.1739226950.git.ashish.kalra@amd.com>
--- arch/x86/include/asm/sev.h | 2 ++ arch/x86/virt/svm/sev.c | 21 ++++++--------------- drivers/iommu/amd/init.c | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 19 deletions(-)
arch/x86/include/asm/sev.h +2 -0
@@ -440,6 +440,7 @@ static inline void snp_update_svsm_ca(void) { }#ifdef CONFIG_KVM_AMD_SEV bool snp_probe_rmptable_info (void ); +int snp_rmptable_init (void );int snp_lookup_rmpentry (u64 pfn,bool *assigned,int *level);void snp_dump_hva_rmpentry (unsigned long address);int psmash (u64 pfn);
@@ -450,6 +451,7 @@ void kdump_sev_callback(void);void snp_fixup_e820_tables (void );#else static inline bool snp_probe_rmptable_info (void ) {return false; } +static inline int snp_rmptable_init (void ) {return -ENOSYS ; }static inline int snp_lookup_rmpentry (u64 pfn,bool *assigned,int *level) {return -ENODEV ; }static inline void snp_dump_hva_rmpentry (unsigned long address) {}static inline int psmash (u64 pfn) {return -ENODEV ; }
arch/x86/virt/svm/sev.c +6 -15
@@ -189,7 +189,7 @@ void __init snp_fixup_e820_tables(void)* described in the SNP_INIT_EX firmware command description in the SNP * firmware ABI spec. */ -static int __init snp_rmptable_init (void ) +int __init snp_rmptable_init (void ) {u64 max_rmp_pfn, calc_rmp_sz, rmptable_size, rmp_end, val;void *rmptable_start;
@@ -197,11 +197,11 @@ static int __init snp_rmptable_init(void)if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))return 0 ; -if (!amd_iommu_snp_en) - goto nosnp; +if (WARN_ON_ONCE (!amd_iommu_snp_en)) +return -ENOSYS ;if (!probed_rmp_size) - goto nosnp; +return -ENOSYS ; rmp_end = probed_rmp_base + probed_rmp_size -1 ;
@@ -218,13 +218,13 @@ static int __init snp_rmptable_init(void)if (calc_rmp_sz > probed_rmp_size) {pr_err ("Memory reserved for the RMP table does not cover full system RAM (expected 0x%llx got 0x%llx)\n" , calc_rmp_sz, probed_rmp_size); - goto nosnp; +return -ENOSYS ; } rmptable_start =memremap (probed_rmp_base, probed_rmp_size,MEMREMAP_WB );if (!rmptable_start) {pr_err ("Failed to map RMP table\n" ); - goto nosnp; +return -ENOSYS ; }/*
@@ -261,17 +261,8 @@ static int __init snp_rmptable_init(void) crash_kexec_post_notifiers = true;return 0 ; - -nosnp: -cc_platform_clear (CC_ATTR_HOST_SEV_SNP ); -return -ENOSYS ; } -/* -* This must be called after the IOMMU has been initialized. -*/ -device_initcall (snp_rmptable_init); -static struct rmpentry *get_rmpentry (u64 pfn) {if (WARN_ON_ONCE (pfn > rmptable_max_pfn))
drivers/iommu/amd/init.c +30 -4
@@ -3219,7 +3219,7 @@ static bool __init detect_ivrs(void)return true; } -static void iommu_snp_enable (void ) +static __init void iommu_snp_enable (void ) {#ifdef CONFIG_KVM_AMD_SEV if (!cc_platform_has (CC_ATTR_HOST_SEV_SNP ))
@@ -3244,6 +3244,14 @@ static void iommu_snp_enable(void) goto disable_snp; } +/* +* Enable host SNP support once SNP support is checked on IOMMU. +*/ +if (snp_rmptable_init ()) { +pr_warn ("SNP: RMP initialization failed, SNP cannot be supported.\n" ); + goto disable_snp; + } +pr_info ("IOMMU SNP support enabled.\n" );return ;
@@ -3381,6 +3389,19 @@ static int __init iommu_go_to_state(enum iommu_init_state state) ret =state_next (); } +/* +* SNP platform initilazation requires IOMMUs to be fully configured. +* If the SNP support on IOMMUs has NOT been checked, simply mark SNP +* as unsupported. If the SNP support on IOMMUs has been checked and +* host SNP support enabled but RMP enforcement has not been enabled +* in IOMMUs, then the system is in a half-baked state, but can limp +* along as all memory should be Hypervisor-Owned in the RMP. WARN, +* but leave SNP as "supported" to avoid confusing the kernel. +*/ +if (ret &&cc_platform_has (CC_ATTR_HOST_SEV_SNP ) && + !WARN_ON_ONCE (amd_iommu_snp_en)) +cc_platform_clear (CC_ATTR_HOST_SEV_SNP ); +return ret; }
@@ -3489,18 +3510,23 @@ void __init amd_iommu_detect(void)int ret;if (no_iommu || (iommu_detected && !gart_iommu_aperture)) -return ; + goto disable_snp;if (!amd_iommu_sme_check ()) -return ; + goto disable_snp; ret =iommu_go_to_state (IOMMU_IVRS_DETECTED );if (ret) -return ; + goto disable_snp; amd_iommu_detected = true; iommu_detected =1 ; x86_init.iommu.iommu_init = amd_iommu_init; +return ; + +disable_snp: +if (cc_platform_has (CC_ATTR_HOST_SEV_SNP )) +cc_platform_clear (CC_ATTR_HOST_SEV_SNP ); }/****************************************************************************
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 373/403 rust: rust_is_available: warn for `bindgen` < 0.72.1 && libclang >= 22
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miguel Ojeda <[email protected]>
[ Upstream commit dc01dfb37b34beeefcfe1c3055364d41a4070c7e ]
Starting with LLVM 22, clang_getTypeDeclaration() may return a forward declaration instead of the type definition. This made bindgen generate opaque types [1][2], which in turn made us fail with e.g.
error[E0609]: no field `__bindgen_anon_1` on type `bindings::kernel_param`
--> rust/kernel/module_param.rs:78:46
|
78 | let container = unsafe { &*((*param).__bindgen_anon_1.arg.cast::<SetOnce<T>>()) };
| ^^^^^^^^^^^^^^^^ unknown field
|
= note: available field is: `_address`This was fixed in bindgen 0.72.1 [3].
In order to clarify what is going on and avoid confusion [4][5], add a warning to rust_is_available.sh about it when the versions match, similar to past warnings like the one removed in:
commit ae64324ad5c1 ("rust: rust_is_available: remove warning for `bindgen` < 0.69.5 && libclang >= 19.1")In addition, even if the versions match, check if the issue appears to not reproduce with the given binaries, to avoid a warning in such a case.
Finally, include tests.
[ Nathan, in parallel, updated the instructions of the LLVM+Rust
kernel.org toolchains [6] so that --version is not passed to cargo for bindgen, and thus the latest bindgen is installed by default, which should help to avoid some of these situations.
Thanks!
- Miguel ]
--- scripts/rust_is_available.sh | 14 +++++++++ .../rust_is_available_bindgen_libclang_22.h | 5 ++++ scripts/rust_is_available_test.py | 30 ++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 scripts/rust_is_available_bindgen_libclang_22.h
scripts/rust_is_available.sh +14 -0
@@ -240,6 +240,20 @@ if [ "$bindgen_libclang_cversion" -ge 1900100 ] && fi fi +if [ "$bindgen_libclang_cversion" -ge 2200000 ] && + [ "$rust_bindings_generator_cversion" -lt 7201 ]; then + # Distributions may have patched the issue. + if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_22.h | grep -q 'pub foo'; then + echo >&2 "***" + echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.72.1 together with libclang >= 22" + echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/3278)." + echo >&2 "*** Your bindgen version: $rust_bindings_generator_version" + echo >&2 "*** Your libclang version: $bindgen_libclang_version" + echo >&2 "***" + warning=1 + fi +fi + # If the C compiler is Clang, then we can also check whether its version # matches the `libclang` version used by the Rust bindings generator. #
scripts/rust_is_available_bindgen_libclang_22.h new file +5 -0
@@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +struct S ; +struct S { +int foo; +};
scripts/rust_is_available_test.py +29 -1
@@ -54,7 +54,7 @@ else: """) @classmethod - def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False): + def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False, libclang_22_patched=False): if libclang_stderr is None: libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})" else:
@@ -70,6 +70,11 @@ else: else: libclang_concat_case = "pass" + if libclang_22_patched: + libclang_22_case = "print('pub foo: ::std::os::raw::c_int,')" + else: + libclang_22_case = "pass" + return cls.generate_executable(f"""#!/usr/bin/env python3 import sys if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv):
@@ -78,6 +83,8 @@ elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv): {version_0_66_case} elif "rust_is_available_bindgen_libclang_concat.h" in " ".join(sys.argv): {libclang_concat_case} +elif "rust_is_available_bindgen_libclang_22.h" in " ".join(sys.argv): + {libclang_22_case} else: print({repr(version_stdout)}) """)
@@ -300,6 +307,27 @@ else: bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_concat_patched=True) result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc }) + def test_bindgen_bad_libclang_22(self): + for (bindgen_version, libclang_version, expected_not_patched) in ( + ("0.71.1", "21.1.0", self.Expected.SUCCESS), + ("0.71.1", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS), + ("0.71.1", "22.1.0", self.Expected.SUCCESS_WITH_WARNINGS), + + ("0.72.0", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS), + + ("0.72.1", "22.0.0", self.Expected.SUCCESS), + ): + with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version): + cc = self.generate_clang(f"clang version {libclang_version}") + libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false" + bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr) + result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc }) + if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS: + self.assertIn(f"Rust bindings generator '{bindgen}' < 0.72.1 together with libclang >= 22", result.stderr) + + bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_22_patched=True) + result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc }) + def test_clang_matches_bindgen_libclang_different_bindgen(self): bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false") result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen })
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 374/403 KVM: selftests: Remove duplicate LAUNCH_UPDATE_VMSA call in SEV-ES migrate test
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
[ Upstream commit 25a642b6abc98bbbabbf2baef9fc498bbea6aee6 ]
Drop the explicit KVM_SEV_LAUNCH_UPDATE_VMSA call when creating an SEV-ES VM in the SEV migration test, as sev_vm_create() automatically updates the VMSA pages for SEV-ES guests. The only reason the duplicate call doesn't cause visible problems is because the test doesn't actually try to run the vCPUs. That will change when KVM adds a check to prevent userspace from re-launching a VMSA (which corrupts the VMSA page due to KVM writing encrypted private memory).
--- tools/testing/selftests/kvm/x86_64/sev_migrate_tests.c | 2 -- 1 file changed, 2 deletions(-)
tools/testing/selftests/kvm/x86_64/sev_migrate_tests.c +0 -2
@@ -36,8 +36,6 @@ static struct kvm_vm *sev_vm_create(bool es)sev_vm_launch (vm, es ?SEV_POLICY_ES :0 ); -if (es) -vm_sev_ioctl (vm,KVM_SEV_LAUNCH_UPDATE_VMSA ,NULL );return vm; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 375/403 PCI: hv: Set irq_retrigger callback for the Hyper-V PCI MSI irqchip
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Naman Jain <[email protected]>
[ Upstream commit 86bdd16e8f390d51bae9e77a4bc4164ca2f580fe ]
The Hyper-V vPCI MSI irqchip never installs an irq_retrigger() callback.
On CPU hot-unplug fixup_irqs() migrates the interrupts which are affine to the outgoing CPU to a new target. If an interrupt still has its pending bit set in the outgoing CPU's IRR at that point, fixup_irqs() resends it on the new target through the irqchip's irq_retrigger() callback. As the Hyper-V PCI/MSI chip does not provide that callback, the pending interrupt is silently dropped, which can result in lost interrupts, stalls and "No irq handler for vector" messages during CPU hotplug.
Install irq_chip_retrigger_hierarchy() as the irq_retrigger() callback for the Hyper-V PCI/MSI irqchip, so that a pending interrupt is resent on its new target CPU via the parent x86 vector domain.
--- drivers/pci/controller/pci-hyperv.c | 1 + 1 file changed, 1 insertion(+)
drivers/pci/controller/pci-hyperv.c +1 -0
@@ -2051,6 +2051,7 @@ static struct irq_chip hv_msi_irq_chip = { .name = "Hyper-V PCIe MSI", .irq_compose_msi_msg = hv_compose_msi_msg, .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_retrigger = irq_chip_retrigger_hierarchy,#ifdef CONFIG_X86 .irq_ack = irq_chip_ack_parent,#elif defined(CONFIG_ARM64 )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 376/403 arch_numa: avoid false positive fortify warning in setup_node_to_cpumask_map()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nathan Chancellor <[email protected]>
commit f2b1cb39d5ccab090d8353788f186f7e7a1fffd4 upstream.
When building ARCH=riscv using clang with CONFIG_FORTIFY_SOURCE and CONFIG_UBSAN_BOUNDS enabled, CONFIG_NR_CPUS > 64, and the default value of 2 for CONFIG_NODES_SHIFT, there is a compiletime warning from the fortify routines.
In file included from mm/arch_numa.c:11:
In file included from include/linux/acpi.h:14:
In file included from include/linux/resource_ext.h:11:
In file included from include/linux/slab.h:17:
In file included from include/linux/gfp.h:7:
In file included from include/linux/mmzone.h:8:
In file included from include/linux/spinlock.h:60:
In file included from include/linux/interrupt_rc.h:17:
In file included from include/linux/smp.h:13:
In file included from include/linux/cpumask.h:11:
In file included from include/linux/bitmap.h:13:
In file included from include/linux/string.h:383:
include/linux/fortify-string.h:430:4: warning: call to '__write_overflow_field' declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Wattribue-warning]
430 | __write_overflow_field(p_size_field, size);
| ^
include/linux/fortify-string.h:430:4: note: called by function 'fortify_memset_chk(unsigned long, unsigned long, unsigned long)'
include/linux/bitmap.h:248:3: note: inlined by function 'setup_node_to_cpumask_map'
248 | memset(dst, 0, len);
| ^
include/linux/fortify-string.h:462:25: note: expanded from macro 'memset'
462 | #define memset(p, c, s) __fortify_memset_chk(p, c, s, \
| ^
include/linux/fortify-string.h:453:2: note: expanded from macro '__fortify_memset_chk'
453 | fortify_memset_chk(__fortify_size, p_size, p_size_field), \
| ^
include/linux/fortify-string.h:430:4: note: use '-gline-directives-only' (implied by '-g1') or higher for more accurate inlining chain locations
430 | __write_overflow_field(p_size_field, size);
| ^1 warning generated.
In this configuration, MAX_NUMNODES is 4. clang unrolls the for loop in setup_node_to_cpumask_map() past this, which triggers the fortify check when accessing node_to_cpumask_map on the theoretical fifth loop iteration because it would be an out of bounds write.
Make it clear to clang that nr_node_ids is bounded by MAX_NUMNODES due to the logic in setup_nr_node_ids() by early returning in setup_node_to_cpumask_map() should that condition be violated.
--- drivers/base/arch_numa.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/base/arch_numa.c +12 -0
@@ -105,6 +105,18 @@ static void __init setup_node_to_cpumaskif (nr_node_ids ==MAX_NUMNODES )setup_nr_node_ids (); +/* +* This check should never be true but it makes it clear to compilers +* that node_to_cpumask_map is bound by nr_node_ids, avoiding false +* positive fortify warnings when accessing node_to_cpumask_map in the +* for loop below. +*/ +if (unlikely (nr_node_ids >MAX_NUMNODES )) { +pr_err ("nr_node_ids (%u) is larger than MAX_NUMNODES (%u)\n" , + nr_node_ids,MAX_NUMNODES ); +return ; + } +/* allocate and clear the mapping */ for (node =0 ; node < nr_node_ids; node++) {alloc_bootmem_cpumask_var (&node_to_cpumask_map[node]);
Greg Kroah-Hartman
maintainerPATCH 377/403 dm-stats: fix a crash if allocation of per-cpu data fails
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikulas Patocka <[email protected]>
commit cc87e26d9cce22061dc21e51e11afef29dbbc36a upstream.
If "dm_kvzalloc(percpu_alloc_size, cpu_to_node(cpu))" fails, the code jumps to the "out" label and calls dm_stat_free. dm_stat_free does "for_each_possible_cpu(cpu) { dm_kvfree(s->stat_percpu[cpu][0].histogram, s->histogram_alloc_size);", which crashes with NULL pointer dereference if s->stat_percpu[cpu] is NULL.
This commit fixes the bug by testing s->stat_percpu[cpu] for NULL before using it.
--- drivers/md/dm-stats.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/md/dm-stats.c +4 -2
@@ -178,8 +178,10 @@ static void dm_stat_free(struct rcu_headkfree (s->program_id);kfree (s->aux_data);for_each_possible_cpu (cpu) { -dm_kvfree (s->stat_percpu[cpu][0 ].histogram, s->histogram_alloc_size); -dm_kvfree (s->stat_percpu[cpu], s->percpu_alloc_size); +if (s->stat_percpu[cpu]) { +dm_kvfree (s->stat_percpu[cpu][0 ].histogram, s->histogram_alloc_size); +dm_kvfree (s->stat_percpu[cpu], s->percpu_alloc_size); + } }dm_kvfree (s->stat_shared[0 ].tmp.histogram, s->histogram_alloc_size);dm_kvfree (s, s->shared_alloc_size);
Greg Kroah-Hartman
maintainerPATCH 378/403 dm-switch: use WRITE_ONCE() in switch_region_table_write()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haotian Zhang <[email protected]>
commit c7391ebe33162c7962b313caea4d8e6b0bc2a671 upstream.
switch_region_table_read() accesses the region table with READ_ONCE() and is called from the lockless switch_map() IO path. However, switch_region_table_write() stores to the same array with a plain assignment. This results in an inconsistent access pattern for a lockless shared variable and may trigger data race reports.
Use WRITE_ONCE() to pair with the existing READ_ONCE() in switch_region_table_read().
--- drivers/md/dm-switch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/md/dm-switch.c +1 -1
@@ -184,7 +184,7 @@ static void switch_region_table_write(st pte = sctx->region_table[region_index]; pte &= ~((((region_table_slot_t )1 << sctx->region_table_entry_bits) -1 ) << bit); pte |= (region_table_slot_t )value << bit; - sctx->region_table[region_index] = pte; +WRITE_ONCE (sctx->region_table[region_index], pte); }/*
Greg Kroah-Hartman
maintainerPATCH 379/403 i3c: master: Fix info leak and UAF in device unregister path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Hunter <[email protected]>
commit d2c743efd2d1ee64e94324664808f623dd865872 upstream.
i3c_master_unregister_i3c_devs() clears i3cdev->dev->desc before calling device_unregister(). During device_unregister(), device_del() emits a KOBJ_REMOVE uevent and unbinds the driver while the device descriptor is still expected to be valid. As a result, i3c_device_uevent() and a racing modalias_show() can observe a NULL desc and fall back to an uninitialized stack struct i3c_device_info, leaking kernel stack contents in the generated modalias. Driver .remove() callbacks may also encounter an unexpected NULL desc during unbind.
Keep desc valid until device_unregister() has completed. Since device_unregister() drops the device reference and may free the device, take an extra reference with get_device() before unregistering. Clear desc afterwards and release the extra reference with put_device(). This preserves the release-time invariant that desc must be NULL while avoiding both the information leak and a potential use-after-free from writing desc after the device has been released.
--- drivers/i3c/master.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/i3c/master.c +5 -4
@@ -2546,11 +2546,12 @@ static void i3c_master_unregister_i3c_deif (!i3cdev->dev)continue ; - i3cdev->dev->desc =NULL ; -if (device_is_registered (&i3cdev->dev->dev)) +if (device_is_registered (&i3cdev->dev->dev)) { +get_device (&i3cdev->dev->dev);device_unregister (&i3cdev->dev->dev); -else -put_device (&i3cdev->dev->dev); + } + i3cdev->dev->desc =NULL ; +put_device (&i3cdev->dev->dev); i3cdev->dev =NULL ; } }
Greg Kroah-Hartman
maintainerPATCH 380/403 i3c: master: svc: bound IBI payload to the requested max_payload_len
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit e2bda39d7f9f285ec803e200b5c1f17143d0b483 upstream.
svc_i3c_master_handle_ibi() reads the IBI payload from the RX FIFO into the IBI slot. The loop is bounded by the hardware FIFO size (SVC_I3C_FIFO_SIZE), not by the slot size.
slot->data points into the IBI pool, which i3c_generic_ibi_alloc_pool() sizes at max_payload_len per slot. svc_i3c_master_request_ibi() only rejects a max_payload_len larger than SVC_I3C_FIFO_SIZE, so a driver can request a smaller one. mctp-i3c requests 1. Each readsb() then copies the controller RXCOUNT bytes (up to 31) with no check against the slot size. A device that sends more bytes than the slot holds writes past slot->data, an out-of-bounds write into the IBI pool.
Bound the loop by dev->ibi->max_payload_len and clamp each read to the space left in the slot, the same way dw-i3c does. A device can still send more than the requested payload. Flush the leftover bytes from the RX FIFO so they do not leak into the next transfer.
--- drivers/i3c/master/svc-i3c-master.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/i3c/master/svc-i3c-master.c +9 -1
@@ -388,14 +388,22 @@ static int svc_i3c_master_handle_ibi(str buf = slot->data;while (SVC_I3C_MSTATUS_RXPEND (readl (master->regs +SVC_I3C_MSTATUS )) && - slot->len <SVC_I3C_FIFO_SIZE ) { + slot->len < dev->ibi->max_payload_len) { mdatactrl =readl (master->regs +SVC_I3C_MDATACTRL ); count =SVC_I3C_MDATACTRL_RXCOUNT (mdatactrl); + count =min (count, dev->ibi->max_payload_len - slot->len);readsb (master->regs +SVC_I3C_MRDATAB , buf, count); slot->len += count; buf += count; } +/* +* The device may have sent more than the requested payload. Drop the +* extra bytes so they do not leak into the next transfer. +*/ +if (SVC_I3C_MSTATUS_RXPEND (readl (master->regs +SVC_I3C_MSTATUS ))) +writel (SVC_I3C_MDATACTRL_FLUSHRB , master->regs +SVC_I3C_MDATACTRL ); + master->ibi.tbq_slot = slot;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 381/403 i3c: master: Fix potential UAF in i3c_device_uevent()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Hunter <[email protected]>
commit e5e8dd2e959f470524c16ca444d001c90d6bb3ad upstream.
i3c_device_uevent() dereferences i3cdev->desc without holding the bus normal-use lock. Since the descriptor pointer can be replaced concurrently, including when a uevent is generated from sysfs, this can result in dereferencing a stale descriptor and lead to a use-after-free.
Use i3c_device_get_info() instead, which protects access to the descriptor with the normal-use lock.
Commit 6cf7b65f7029 ("i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock") replaced the accessor with a direct descriptor dereference because i3c_device_get_info() would recursively acquire bus->lock during device registration.
This change depends on "i3c: master: Fix recursive locking during device registration", which moves device registration out from under bus->lock and removes the possibility of that deadlock. Without that change, restoring the i3c_device_get_info() call would reintroduce the deadlock.
--- drivers/i3c/master.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/i3c/master.c +1 -2
@@ -282,8 +282,7 @@ static int i3c_device_uevent(const strucstruct i3c_device_info devinfo;u16 manuf, part, ext; -if (i3cdev->desc) - devinfo = i3cdev->desc->info; +i3c_device_get_info (i3cdev, &devinfo); manuf =I3C_PID_MANUF_ID (devinfo.pid); part =I3C_PID_PART_ID (devinfo.pid); ext =I3C_PID_EXTRA_INFO (devinfo.pid);
Re: [PATCH 6.12 381/403] i3c: master: Fix potential UAF in i3c_device_uevent()
Hi Greg/Sasha,
Quoted 41 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adrian Hunter <[email protected]>
commit
e5e8dd2e959f470524c16ca444d001c90d6bb3adupstream.
i3c_device_uevent()dereferencesi3cdev->descwithout holding the bus normal-use lock. Since the descriptor pointer can be replaced concurrently, including when a uevent is generated from sysfs, this can result in dereferencing a stale descriptor and lead to a use-after-free.Use
i3c_device_get_info()instead, which protects access to the descriptor with the normal-use lock.Commit
6cf7b65f7029("i3c: Usei3cdev->desc->infoinstead of callingi3c_device_get_info()to avoid deadlock") replaced the accessor with a direct descriptor dereference becausei3c_device_get_info()would recursively acquirebus->lockduring device registration.This change depends on "i3c: master: Fix recursive locking during device registration", which moves device registration out from under
bus->lockand removes the possibility of that deadlock. Without that change, restoring thei3c_device_get_info()call would reintroduce the deadlock.Fixes 6cf7b65f7029 ("i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock")Cc [email protected] # requires "i3c: master: Fix recursive locking during device registration"Signed-off-by Adrian Hunter <[email protected]>Acked-by Mukesh Savaliya <[email protected]>Reviewed-by Frank Li <[email protected]>Signed-off-by Alexandre Belloni <[email protected]>Signed-off-by Greg Kroah-Hartman <[email protected]>--- drivers/i3c/master.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/i3c/master.c static int i3c_device_uevent(const struc · line 285
Quoted 6 lines
drivers/i3c/master.c +1
-2@@ -282,8 +282,7 @@ static int i3c_device_uevent(const strucstruct i3c_device_info devinfo;u16 manuf, part, ext; -if (i3cdev->desc) - devinfo = i3cdev->desc->info; +i3c_device_get_info (i3cdev, &devinfo);
I have run an AI-assisted backport review and spotted an issue.
this commit restores:
i3c_device_get_info(i3cdev, &devinfo);
This function takes the I3C bus lock. However, 6.12.y still registers devices while holding the same lock:
i3c_bus_normaluse_lock(&master->bus);
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);The uevent can therefore acquire the lock recursively and deadlock.
Upstream avoids this with prerequisite commit: 456f832e5fc2. Maybe we should drop this patch without prerequisites.
Thoughts?
thanks,
Harshit
Quoted 6 lines
drivers/i3c/master.c +0
-0manuf =I3C_PID_MANUF_ID (devinfo.pid); part =I3C_PID_PART_ID (devinfo.pid); ext =I3C_PID_EXTRA_INFO (devinfo.pid);
Sasha Levin
maintainerRe: [PATCH 6.12 381/403] i3c: master: Fix potential UAF in i3c_device_uevent()
Quoted 2 lines
Upstream avoids this with prerequisite commit:
456f832e5fc2. Maybe we should drop this patch without prerequisites.
Dropped from all seven queues. Upstream's own commit carries "Cc:
[email protected] # requires \"i3c: master: Fix recursive locking during device registration\"" (456f832e5fc2), and that prerequisite isn't queued anywhere - so it needs to go until both land together. Thanks for catching it.
Signature · 2 lines
Thanks, Sasha
Re: [PATCH 6.12 381/403] i3c: master: Fix potential UAF in i3c_device_uevent()
Quoted 9 lines · Sasha Levin
Quoted 2 lines
Upstream avoids this with prerequisite commit:
456f832e5fc2. Maybe we should drop this patch without prerequisites.Dropped from all seven queues. Upstream's own commit carries "Cc:
[email protected] # requires \"i3c: master: Fix recursive locking during device registration\"" (456f832e5fc2), and that prerequisite isn't queued anywhere - so it needs to go until both land together. Thanks for catching it.
AFAICT it was queued normally and is in Linus' tree:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=456f832e5fc26fbfd3b8200fd4553eee520cc377
Sasha Levin
maintainerRe: [PATCH 6.12 381/403] i3c: master: Fix potential UAF in i3c_device_uevent()
Quoted 3 lines
AFAICT it was queued normally and is in Linus' tree:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=456f832e5fc26fbfd3b8200fd4553eee520cc377
Right, it's upstream, but that's not the same as being queued in any of our stable trees. I checked all seven active branches (7.2, 6.18, 6.12, 6.6, 6.1, 5.15, 5.10) and none of them carry it.
It's the first of a six commit i3c hardening series:
456f832e5fc2 ("i3c: master: Fix recursive locking during device registration") 8bed7f4fa710 ("i3c: Fix unlocked dereference of dev->desc in i3c_device_get_supported_xfer_mode()") 4dc1b3eeba79 (...)
feb0ed76601f (...)
e5e8dd2e959f ("i3c: master: Fix potential UAF in i3c_device_uevent()") <- the patch in question f44d3b15326c (...)
Of those, only 8bed7f4fa710 has landed anywhere (7.2 alone). Applying e5e8dd2e959f on its own trades the UAF it fixes for an easily-triggered recursive-rwsem deadlock on ordinary device bind/unbind, so I've dropped it from all seven queues again.
Could you (or whoever's carrying the i3c tree) send a properly ordered backport of the whole series instead of the single commit?
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 382/403 wifi: brcmfmac: Fix memory leak in brcmf_sdio_read_control()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 0d10db8e94fcb23a799789aaa696b4d8f937e207 upstream.
The memory allocated for buf is not freed in some of the error paths in brcmf_sdio_read_control(). Fix that by adding vfree() calls.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +2 -1
@@ -1826,17 +1826,18 @@ gotpkt:if (bus->rxctl) {brcmf_err ("last control frame is being processed.\n" );spin_unlock_bh (&bus->rxctl_lock); -vfree (buf); goto done; } bus->rxctl = buf + doff; bus->rxctl_orig = buf; bus->rxlen = len - doff;spin_unlock_bh (&bus->rxctl_lock); + buf =NULL ; done:/* Awake any waiters */ brcmf_sdio_dcmd_resp_wake (bus); +vfree (buf); }/* Pad read to blocksize for efficiency */
Greg Kroah-Hartman
maintainerPATCH 383/403 wifi: rtl8xxxu: fix use-after-free from rx_urb_wq on stop
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 6c080026ecc17eecb103f8927c64ea73a74bb818 upstream.
rtl8xxxu arms rx_urb_wq from the RX completion path:rtl8xxxu_rx_complete() hands the URB to rtl8xxxu_queue_rx_urb(), which queues it on rx_urb_pending_list and, once the list grows past RTL8XXXU_RX_URB_PENDING_WATER, schedules rx_urb_wq. The worker rtl8xxxu_rx_urb_work() drains rx_urb_pending_list, recovers priv through container_of, and resubmits each URB through rtl8xxxu_submit_rx_urb(), which anchors it on rx_anchor and dereferences priv->udev.
rtl8xxxu_stop() cancels the sibling work items (c2hcmd_work, ra_watchdog, update_beacon_work) but never cancels rx_urb_wq, so a worker armed during the last burst of RX traffic can run rtl8xxxu_rx_urb_work() after rtl8xxxu_disconnect() has called ieee80211_free_hw(), which frees priv, producing a use-after-free. The window opens under active RX traffic (pending count above the watermark) followed by a disconnect.
There are two teardown races to close:
* rtl8xxxu_queue_rx_urb() decided whether to enqueue under rx_urb_lock but called schedule_work() after dropping the lock. A completion that observed shutdown == false and released the lock could then call schedule_work() after rtl8xxxu_stop() had set shutdown and cancel_work_sync() had already returned, arming the worker to run after the teardown. Move schedule_work() under the same !shutdown branch so the arming decision is atomic with the shutdown check.
* rtl8xxxu_rx_urb_work() anchors every URB it drained back onto rx_anchor through rtl8xxxu_submit_rx_urb(). A worker still running when usb_kill_anchored_urbs(&priv->rx_anchor) returned would submit a URB that escaped the kill. In rtl8xxxu_stop(), call cancel_work_sync(&priv->rx_urb_wq) before the kill so the worker is drained first.
After priv->shutdown is set under rx_urb_lock, completions can no longer queue rx_urb_wq. cancel_work_sync() then drains the last queued or running worker, and the following usb_kill_anchored_urbs() kills the URBs it may have submitted.
rtl8xxxu_disconnect() is covered because ieee80211_unregister_hw() guarantees .stop() runs for a live interface before ieee80211_free_hw() frees priv. The probe error path needs no cancel: rx_urb_wq is INIT_WORK()'d there but cannot have been scheduled, since no URB is submitted before ieee80211_register_hw() succeeds.
This bug was found by static analysis.
--- drivers/net/wireless/realtek/rtl8xxxu/core.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-)
drivers/net/wireless/realtek/rtl8xxxu/core.c +14 -5
@@ -5863,14 +5863,19 @@ static void rtl8xxxu_queue_rx_urb(struct {struct sk_buff *skb;unsigned long flags; -int pending =0 ;spin_lock_irqsave (&priv->rx_urb_lock, flags);if (!priv->shutdown) {list_add_tail (&rx_urb->list, &priv->rx_urb_pending_list); priv->rx_urb_pending_count++; - pending = priv->rx_urb_pending_count; +/* +* Arm the worker under rx_urb_lock so this is atomic with the +* shutdown check: moving it out of the lock would let a +* completion arm the work after rtl8xxxu_stop() canceled it. +*/ +if (priv->rx_urb_pending_count >RTL8XXXU_RX_URB_PENDING_WATER ) +schedule_work (&priv->rx_urb_wq); }else { skb = (struct sk_buff *)rx_urb->urb.context;dev_kfree_skb_irq (skb);
@@ -5878,9 +5883,6 @@ static void rtl8xxxu_queue_rx_urb(struct }spin_unlock_irqrestore (&priv->rx_urb_lock, flags); - -if (pending >RTL8XXXU_RX_URB_PENDING_WATER ) -schedule_work (&priv->rx_urb_wq); }static void rtl8xxxu_rx_urb_work (struct work_struct *work)
@@ -7527,6 +7529,13 @@ static void rtl8xxxu_stop(struct ieee802 priv->shutdown = true;spin_unlock_irqrestore (&priv->rx_urb_lock, flags); +/* +* Cancel before killing rx_anchor: the worker re-anchors every URB +* it drained via rtl8xxxu_submit_rx_urb(), so a worker still running +* after the kill could submit a URB that escapes it. +*/ +cancel_work_sync (&priv->rx_urb_wq); +usb_kill_anchored_urbs (&priv->rx_anchor);usb_kill_anchored_urbs (&priv->tx_anchor);if (priv->usb_interrupts)
Greg Kroah-Hartman
maintainerPATCH 384/403 crypto: sun8i-ce - Remove crypto_rng interface
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit 011556f71d094da61379ae3672692cae2795304e upstream.
Since the crypto_rng interface for hardware PRNGs is unused and is redundant with hwrng and the actual Linux RNG, it's being phased out. Most drivers for it were already removed. Go ahead and remove the sun8i-ce support which is one of the only remaining ones.
Note that the sun8i-ce support for hwrng remains in place. That is the interface that actually matters.
As usual for crypto_rng, this driver was also buggy: its ->generate() function had a use-after-free vulnerability due to using wait_for_completion_interruptible_timeout() without handling shutting down the DMA operation if a signal is sent. There's no point in fixing this separately only to remove the code anyway, so this commit is marked with Fixes and Cc stable.
--- drivers/crypto/allwinner/Kconfig | 8 - drivers/crypto/allwinner/sun8i-ce/Makefile | 1 drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c | 57 ------- drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c | 159 ---------------------- drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h | 29 ---- 5 files changed, 254 deletions(-) delete mode 100644 drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c
drivers/crypto/allwinner/Kconfig +0 -8
@@ -70,14 +70,6 @@ config CRYPTO_DEV_SUN8I_CE_HASH help Say y to enable support for hash algorithms. -config CRYPTO_DEV_SUN8I_CE_PRNG - bool "Support for Allwinner Crypto Engine PRNG" - depends on CRYPTO_DEV_SUN8I_CE - select CRYPTO_RNG - help - Select this option if you want to provide kernel-side support for - the Pseudo-Random Number Generator found in the Crypto Engine. - config CRYPTO_DEV_SUN8I_CE_TRNG bool "Support for Allwinner Crypto Engine TRNG" depends on CRYPTO_DEV_SUN8I_CE
drivers/crypto/allwinner/sun8i-ce/Makefile +0 -1
@@ -1,5 +1,4 @@ obj-$(CONFIG_CRYPTO_DEV_SUN8I_CE) += sun8i-ce.o sun8i-ce-y += sun8i-ce-core.o sun8i-ce-cipher.o sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_HASH) += sun8i-ce-hash.o -sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG) += sun8i-ce-prng.o sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG) += sun8i-ce-trng.o
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c +0 -57
141 lines not on this page · raw message
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c deleted +0 -159
159 lines not on this page · raw message
drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h +0 -29
@@ -15,7 +15,6 @@#include <linux/hw_random.h> #include <crypto/internal/hash.h> #include <crypto/md5.h> -#include <crypto/rng.h> #include <crypto/sha1.h> #include <crypto/sha2.h>
@@ -58,9 +57,7 @@#define CE_ALG_SHA384 20#define CE_ALG_SHA512 21#define CE_ALG_TRNG 48 -#define CE_ALG_PRNG 49#define CE_ALG_TRNG_V2 0x1c -#define CE_ALG_PRNG_V2 0x1d/* Used in ce_variant */ #define CE_ID_NOTSUPP 0xFF
@@ -96,10 +93,6 @@#define ESR_H6 4#define ESR_D1 5 -#define PRNG_DATA_SIZE (160 / 8) -#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8) -#define PRNG_LD BIT(17) -#define CE_DIE_ID_SHIFT 16#define CE_DIE_ID_MASK 0x07
@@ -133,13 +126,10 @@ struct ce_clock {* bytes or words * @hash_t_dlen_in_bytes: Does the request size for hash is in * bits or words -* @prng_t_dlen_in_bytes: Does the request size for PRNG is in -* bytes or words * @trng_t_dlen_in_bytes: Does the request size for TRNG is in * bytes or words * @ce_clks: list of clocks needed by this variant * @esr: The type of error register -* @prng: The CE_ALG_XXX value for the PRNG * @trng: The CE_ALG_XXX value for the TRNG */ struct ce_variant {
@@ -148,12 +138,10 @@ struct ce_variant {u32 op_mode[CE_ID_OP_MAX ];bool cipher_t_dlen_in_bytes;bool hash_t_dlen_in_bits; -bool prng_t_dlen_in_bytes;bool trng_t_dlen_in_bytes;bool needs_word_addresses;struct ce_clock ce_clks[CE_MAX_CLOCKS ];int esr; -unsigned char prng;unsigned char trng; };
@@ -313,16 +301,6 @@ struct sun8i_ce_hash_reqctx { };/* -* struct sun8i_ce_prng_ctx - context for PRNG TFM -* @seed: The seed to use -* @slen: The size of the seed -*/ -struct sun8i_ce_rng_tfm_ctx { -void *seed; -unsigned int slen; -}; - -/* * struct sun8i_ce_alg_template - crypto_alg template * @type: the CRYPTO_ALG_TYPE for this template * @ce_algo_id: the CE_ID for this template
@@ -342,7 +320,6 @@ struct sun8i_ce_alg_template {union {struct skcipher_engine_alg skcipher;struct ahash_engine_alg hash; -struct rng_alg rng; } alg;unsigned long stat_req;unsigned long stat_fb;
@@ -383,11 +360,5 @@ int sun8i_ce_hash_finup(struct ahash_reqint sun8i_ce_hash_digest (struct ahash_request *areq);int sun8i_ce_hash_run (struct crypto_engine *engine,void *breq); -int sun8i_ce_prng_generate (struct crypto_rng *tfm,const u8 *src, -unsigned int slen,u8 *dst,unsigned int dlen); -int sun8i_ce_prng_seed (struct crypto_rng *tfm,const u8 *seed,unsigned int slen); -void sun8i_ce_prng_exit (struct crypto_tfm *tfm); -int sun8i_ce_prng_init (struct crypto_tfm *tfm); -int sun8i_ce_hwrng_register (struct sun8i_ce_dev *ce);void sun8i_ce_hwrng_unregister (struct sun8i_ce_dev *ce);
Greg Kroah-Hartman
maintainerPATCH 385/403 crypto: sun8i-ss - Remove crypto_rng interface
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Biggers <[email protected]>
commit a78446ee6fae86ac8733f120e3ffce2e5d9384f5 upstream.
Since the crypto_rng interface for hardware PRNGs is unused and is redundant with hwrng and the actual Linux RNG, it's being phased out. Most drivers for it were already removed. Go ahead and remove the sun8i-ss support which is one of the only remaining ones.
As usual for crypto_rng, this driver was also buggy: its ->generate() function had a use-after-free vulnerability due to using wait_for_completion_interruptible_timeout() without handling shutting down the DMA operation if a signal is sent. Also, it had a buffer overread bug in the line 'memcpy(ctx->seed, d + dlen, ctx->slen);'. There's no point in fixing these bugs separately only to remove the code anyway, so this commit is marked with Fixes and Cc stable.
--- drivers/crypto/allwinner/Kconfig | 8 drivers/crypto/allwinner/sun8i-ss/Makefile | 1 drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c | 39 ---- drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c | 177 ---------------------- drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h | 23 -- 5 files changed, 248 deletions(-) delete mode 100644 drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c
drivers/crypto/allwinner/Kconfig +0 -8
@@ -105,14 +105,6 @@ config CRYPTO_DEV_SUN8I_SS_DEBUG This will create /sys/kernel/debug/sun8i-ss/stats for displaying the number of requests per flow and per algorithm. -config CRYPTO_DEV_SUN8I_SS_PRNG - bool "Support for Allwinner Security System PRNG" - depends on CRYPTO_DEV_SUN8I_SS - select CRYPTO_RNG - help - Select this option if you want to provide kernel-side support for - the Pseudo-Random Number Generator found in the Security System. - config CRYPTO_DEV_SUN8I_SS_HASH bool "Enable support for hash on sun8i-ss" depends on CRYPTO_DEV_SUN8I_SS
drivers/crypto/allwinner/sun8i-ss/Makefile +0 -1
@@ -1,4 +1,3 @@ obj-$(CONFIG_CRYPTO_DEV_SUN8I_SS) += sun8i-ss.o sun8i-ss-y += sun8i-ss-core.o sun8i-ss-cipher.o -sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG) += sun8i-ss-prng.o sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_HASH) += sun8i-ss-hash.o
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c +0 -39
@@ -11,7 +11,6 @@*/ #include <crypto/engine.h> -#include <crypto/internal/rng.h> #include <crypto/internal/skcipher.h> #include <linux/clk.h> #include <linux/delay.h>
@@ -283,25 +282,6 @@ static struct sun8i_ss_alg_template ss_a .do_one_request = sun8i_ss_handle_cipher_request, }, }, -#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG -{ - .type = CRYPTO_ALG_TYPE_RNG, - .alg.rng = { - .base = { - .cra_name = "stdrng", - .cra_driver_name = "sun8i-ss-prng", - .cra_priority = 300, - .cra_ctxsize = sizeof(struct sun8i_ss_rng_tfm_ctx), - .cra_module = THIS_MODULE, - .cra_init = sun8i_ss_prng_init, - .cra_exit = sun8i_ss_prng_exit, - }, - .generate = sun8i_ss_prng_generate, - .seed = sun8i_ss_prng_seed, - .seedsize =PRNG_SEED_SIZE , - } -}, -#endif#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH { .type = CRYPTO_ALG_TYPE_AHASH, .ss_algo_id = SS_ID_HASH_MD5,
@@ -501,12 +481,6 @@ static int sun8i_ss_debugfs_show(structseq_printf (seq,"\tFallback due to SG numbers: %lu\n" , ss_algs[i].stat_fb_sgnum);break ; -case CRYPTO_ALG_TYPE_RNG : -seq_printf (seq,"%s %s reqs=%lu tsize=%lu\n" , - ss_algs[i].alg.rng.base.cra_driver_name, - ss_algs[i].alg.rng.base.cra_name, - ss_algs[i].stat_req, ss_algs[i].stat_bytes); -break ;case CRYPTO_ALG_TYPE_AHASH : seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n", ss_algs[i].alg.hash.base.halg.base.cra_driver_name,
@@ -707,14 +681,6 @@ static int sun8i_ss_register_algs(structreturn err; }break ; -case CRYPTO_ALG_TYPE_RNG : - err =crypto_register_rng (&ss_algs[i].alg.rng); -if (err) { -dev_err (ss->dev,"Fail to register %s\n" , - ss_algs[i].alg.rng.base.cra_name); - ss_algs[i].ss =NULL ; - } -break ;case CRYPTO_ALG_TYPE_AHASH : id = ss_algs[i].ss_algo_id; ss_method = ss->variant->alg_hash[id];
@@ -756,11 +722,6 @@ static void sun8i_ss_unregister_algs(str ss_algs[i].alg.skcipher.base.base.cra_name);crypto_engine_unregister_skcipher (&ss_algs[i].alg.skcipher);break ; -case CRYPTO_ALG_TYPE_RNG : -dev_info (ss->dev,"Unregister %d %s\n" , i, - ss_algs[i].alg.rng.base.cra_name); -crypto_unregister_rng (&ss_algs[i].alg.rng); -break ;case CRYPTO_ALG_TYPE_AHASH :dev_info (ss->dev,"Unregister %d %s\n" , i, ss_algs[i].alg.hash.base.halg.base.cra_name);
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c deleted +0 -177
177 lines not on this page · raw message
drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h +0 -23
@@ -8,7 +8,6 @@#include <crypto/aes.h> #include <crypto/des.h> #include <crypto/engine.h> -#include <crypto/rng.h> #include <crypto/skcipher.h> #include <linux/atomic.h> #include <linux/debugfs.h>
@@ -27,7 +26,6 @@#define SS_ALG_DES (1 << 2)#define SS_ALG_3DES (2 << 2)#define SS_ALG_MD5 (3 << 2) -#define SS_ALG_PRNG (4 << 2)#define SS_ALG_SHA1 (6 << 2)#define SS_ALG_SHA224 (7 << 2)#define SS_ALG_SHA256 (8 << 2)
@@ -68,8 +66,6 @@#define SS_FLOW0 BIT(30)#define SS_FLOW1 BIT(31) -#define SS_PRNG_CONTINUE BIT(18) -#define MAX_SG 8#define MAXFLOW 2
@@ -79,9 +75,6 @@#define SS_DIE_ID_SHIFT 20#define SS_DIE_ID_MASK 0x07 -#define PRNG_DATA_SIZE (160 / 8) -#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8) -#define MAX_PAD_SIZE 4096/*
@@ -214,16 +207,6 @@ struct sun8i_cipher_tfm_ctx { };/* -* struct sun8i_ss_prng_ctx - context for PRNG TFM -* @seed: The seed to use -* @slen: The size of the seed -*/ -struct sun8i_ss_rng_tfm_ctx { -void *seed; -unsigned int slen; -}; - -/* * struct sun8i_ss_hash_tfm_ctx - context for an ahash TFM * @fallback_tfm: pointer to the fallback TFM * @ss: pointer to the private data of driver handling this TFM
@@ -272,7 +255,6 @@ struct sun8i_ss_alg_template {struct sun8i_ss_dev *ss;union {struct skcipher_engine_alg skcipher; -struct rng_alg rng;struct ahash_engine_alg hash; } alg;unsigned long stat_req;
@@ -298,11 +280,6 @@ int sun8i_ss_skencrypt(struct skcipher_rint sun8i_ss_get_engine_number (struct sun8i_ss_dev *ss);int sun8i_ss_run_task (struct sun8i_ss_dev *ss,struct sun8i_cipher_req_ctx *rctx,const char *name); -int sun8i_ss_prng_generate (struct crypto_rng *tfm,const u8 *src, -unsigned int slen,u8 *dst,unsigned int dlen); -int sun8i_ss_prng_seed (struct crypto_rng *tfm,const u8 *seed,unsigned int slen); -int sun8i_ss_prng_init (struct crypto_tfm *tfm); -void sun8i_ss_prng_exit (struct crypto_tfm *tfm);int sun8i_ss_hash_init_tfm (struct crypto_ahash *tfm);void sun8i_ss_hash_exit_tfm (struct crypto_ahash *tfm);
Greg Kroah-Hartman
maintainerPATCH 386/403 wifi: mwifiex: Detach sync cmd buffer on interrupted wait
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fabio Estevam <[email protected]>
commit ef06882c7d8a7400b67d0d003b1008093dd589ed upstream.
mwifiex synchronous commands keep the caller-provided data buffer in cmd_node->data_buf. Several callers pass stack-allocated objects there.
If wait_event_interruptible_timeout() is interrupted, the caller can return and release that stack object while the firmware command is still the current command. A late firmware response then reaches the normal response handler, which can copy data through cmd_node->data_buf into the stale stack address.
This fixes a stack corruption observed during repeated association and disassociation cycles. The panic trace showed the command wait being interrupted immediately before a bad pointer dereference:
cmd_wait_q terminated: -512
Unable to handle kernel paging request at virtual address 002c583837384662 Kernel panic - not syncing: stack-protector: Kernel stack is corrupted ... Tainted: [M]=MACHINE_CHECK
The fault address decodes as little-endian ASCII:
0x002c583837384662 -> "bF878X,\0"
which is a fragment of the VERSION_EXT firmware string exposed as debugfs "verext":
w8997o-V4, RF878X, FP92, 16.92.21.p153.7
The same runs also showed corrupted control data containing:
0x2400372e333531 -> "153.7\0$"
which is the tail of the same VERSION_EXT string. This points at a late VERSION_EXT response writing through a stale stack-backed data_buf after the interrupted wait returned.
After cancelling pending commands on an interrupted or timed-out wait, detach the caller-owned data buffer from the still-current command. This preserves the existing command cancellation behaviour while preventing a late response from writing through a pointer whose lifetime ended with the waiting caller.
Tested on an i.MX8MP board using an 88W8997.
--- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +12 -0
@@ -57,6 +57,18 @@ int mwifiex_wait_queue_complete(struct mmwifiex_dbg (adapter,ERROR ,"cmd_wait_q terminated: %d\n" , status);mwifiex_cancel_all_pending_cmd (adapter); + +/* The command response path writes through cmd_node->data_buf. +* On an interrupted wait, the caller can return and release a +* stack-allocated data_buf before a late firmware response is +* processed. Detach the caller-owned buffer from the current +* command so a late response cannot corrupt freed stack memory. +*/ +spin_lock_bh (&adapter->mwifiex_cmd_lock); +if (adapter->curr_cmd == cmd_queued) + adapter->curr_cmd->data_buf =NULL ; +spin_unlock_bh (&adapter->mwifiex_cmd_lock); +return status; }
Greg Kroah-Hartman
maintainerPATCH 387/403 wifi: rtl818x: initialize eeprom_93cx6 struct to zero
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stanislaw Gruszka <[email protected]>
commit 799b5f45cb8194ebd06c9c89e0afdad5bedd2cc5 upstream.
Commit 7738a7ab9d12 ("misc: eeprom: eeprom_93cx6: Add quirk for extra read clock cycle") added extra 'quirk' field to struct eeprom_93cx6.
Many existing users of eeprom_93cx6, including rtl818x drivers, allocate the structure on the stack without initializing all fields. As a result, the added quirk field has an undefined value and can randomly cause reading wrong data from the EEPROM.
Fix by initializing the structures with {}.
--- drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 2 +- drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +1 -1
@@ -1652,7 +1652,7 @@ static void rtl8180_eeprom_register_writstatic void rtl8180_eeprom_read (struct rtl8180_priv *priv) { -struct eeprom_93cx6 eeprom; +struct eeprom_93cx6 eeprom = {};int eeprom_cck_table_adr;u16 eeprom_val;int i;
drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +1 -1
@@ -1445,7 +1445,7 @@ static int rtl8187_probe(struct usb_intestruct usb_device *udev =interface_to_usbdev (intf);struct ieee80211_hw *dev;struct rtl8187_priv *priv; -struct eeprom_93cx6 eeprom; +struct eeprom_93cx6 eeprom = {};struct ieee80211_channel *channel;const char *chip_name;u16 txpwr, reg;
Greg Kroah-Hartman
maintainerPATCH 388/403 wifi: rtlwifi: rtl8192du: check QoS TID before indexing tids
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit ed4f05d9f2f42fd866f55108db8123eefcc5fb33 upstream.
rtl92du_tx_fill_desc() uses ieee80211_get_tid() to read the QoS TID from the 802.11 header and then uses it as an index into sta_entry->tids[]. ieee80211_get_tid() returns the low 4-bit QoS TID value, so the result can be in the range 0..15.
rtlwifi only allocates MAX_TID_COUNT entries for sta_entry->tids[], and MAX_TID_COUNT is 9. A QoS TID greater than 8 therefore indexes past the aggregation state array. Keep the default RTL_AGG_STOP state for out-of-range TIDs, matching rtl92cu_tx_fill_desc().
This issue was detected by our static analysis tool and confirmed by manual audit. UBSAN validation for the same bug pattern reports an array-index-out-of-bounds access with index 10 for type 'rtl_tid_data [9]'.
--- drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/net/wireless/realtek/rtlwifi/rtl8192du/trx.c +2 -1
@@ -106,7 +106,8 @@ void rtl92du_tx_fill_desc(struct ieee802if (sta) { sta_entry = (struct rtl_sta_info *)sta->drv_priv; tid =ieee80211_get_tid (hdr); - agg_state = sta_entry->tids[tid].agg.agg_state; +if (tid <MAX_TID_COUNT ) + agg_state = sta_entry->tids[tid].agg.agg_state; ampdu_density = sta->deflink.ht_cap.ampdu_density; }
Greg Kroah-Hartman
maintainerPATCH 389/403 wifi: rtlwifi: rtl8192du: Fix possible memory leak in rtl92du_init_sw_vars()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 6496ce90845df2d22fb8e8ed235cd2936fad41c8 upstream.
The memory allocated inside rtl92du_init_shared_data() is not freed in any of the subsequent error paths in rtl92du_init_sw_vars(). Fix that by adding a call to rtl92du_deinit_shared_data() in the error path.
--- drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c +9 -3
@@ -147,8 +147,10 @@ static int rtl92du_init_sw_vars(struct i/* for firmware buf */ rtlpriv->rtlhal.pfirmware =kmalloc (0x8000 ,GFP_KERNEL ); -if (!rtlpriv->rtlhal.pfirmware) -return -ENOMEM ; +if (!rtlpriv->rtlhal.pfirmware) { + err = -ENOMEM ; + goto error; + } rtlpriv->max_fw_size =0x8000 ;pr_info ("Driver for Realtek RTL8192DU WLAN interface\n" );
@@ -162,10 +164,14 @@ static int rtl92du_init_sw_vars(struct ipr_err ("Failed to request firmware!\n" );kfree (rtlpriv->rtlhal.pfirmware); rtlpriv->rtlhal.pfirmware =NULL ; -return err; + goto error; }return 0 ; + +error: +rtl92du_deinit_shared_data (hw); +return err; }static void rtl92du_deinit_sw_vars (struct ieee80211_hw *hw)
Greg Kroah-Hartman
maintainerPATCH 390/403 wifi: rtw88: Fix potential memory leak in rtw_txq_push_skb()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 9f2948010764d708bda27369d09ce6f194abe8e3 upstream.
The skb passed to the rtw_hci_tx_write() is expected to be freed when the function fails, but the error path in rtw_txq_push_skb() does not free the skb before returning. This can lead to a memory leak in rtw_txq_push() where a dequeued skb is passed to rtw_txq_push_skb().
--- drivers/net/wireless/realtek/rtw88/tx.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/wireless/realtek/rtw88/tx.c +1 -0
@@ -614,6 +614,7 @@ static int rtw_txq_push_skb(struct rtw_d ret =rtw_hci_tx_write (rtwdev, &pkt_info, skb);if (ret) {rtw_err (rtwdev,"failed to write TX skb to HCI\n" ); +ieee80211_free_txskb (rtwdev->hw, skb);return ret; }return 0 ;
Greg Kroah-Hartman
maintainerPATCH 391/403 wifi: rtw88: pci: fix resource leak on failed NAPI setup
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
commit e779df4806cd29cbcca5c9dc0a1073662c76b889 upstream.
rtw_pci_probe() allocates PCI resources through rtw_pci_setup_resource() before it sets up NAPI. If rtw_pci_napi_init() fails, the error path jumps straight to err_pci_declaim and skips rtw_pci_destroy(), leaving the PCI resources allocated by rtw_pci_setup_resource() behind.
Add a dedicated cleanup label for the NAPI setup failure path so probe destroys the PCI resources.
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing current mainline kernels. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc7.
An x86_64 allyesconfig build showed no new warnings. As we do not have a suitable rtw88 PCI board to test with, no runtime testing was able to be performed.
--- drivers/net/wireless/realtek/rtw88/pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/net/wireless/realtek/rtw88/pci.c +3 -1
@@ -1762,7 +1762,7 @@ int rtw_pci_probe(struct pci_dev *pdev, ret =rtw_pci_napi_init (rtwdev);if (ret) {rtw_err (rtwdev,"failed to setup NAPI\n" ); - goto err_pci_declaim; + goto err_destroy_rsrc; } ret =rtw_chip_info_setup (rtwdev);
@@ -1794,6 +1794,8 @@ int rtw_pci_probe(struct pci_dev *pdev, err_destroy_pci:rtw_pci_napi_deinit (rtwdev); + +err_destroy_rsrc:rtw_pci_destroy (rtwdev, pdev); err_pci_declaim:
Greg Kroah-Hartman
maintainerPATCH 392/403 wifi: mt76: mt7615: avoid waiting for mac work under the mt76 mutex
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Runyu Xiao <[email protected]>
commit bda8324270b1ac91bfba1df8928e0570e29759e8 upstream.
mt7615_suspend() acquired the mt76 mutex and then called cancel_delayed_work_sync() on mac_work. mt7615_mac_work() acquires the same mutex via mt7615_mutex_acquire() at the top of the worker, so if mac_work is already running and blocked on the mutex, the suspend path deadlocks waiting for the work it holds the mutex against.
Flush scan_work and mac_work before taking the mutex, matching the suspend paths in mt7921 and mt7925. scan_work only takes the mt76 spinlock, but moving it keeps the sequence consistent. This also keeps mac_work from running over an already suspended HIF, which the previous split (async cancel under the lock, sync cancel after release) would have allowed.
--- drivers/net/wireless/mediatek/mt76/mt7615/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7615/main.c +3 -2
@@ -1237,11 +1237,12 @@ static int mt7615_suspend(struct ieee802cancel_delayed_work_sync (&dev->pm.ps_work);mt76_connac_free_pending_tx_skbs (&dev->pm,NULL ); +cancel_delayed_work_sync (&phy->scan_work); +cancel_delayed_work_sync (&phy->mt76->mac_work); +mt7615_mutex_acquire (dev);clear_bit (MT76_STATE_RUNNING , &phy->mt76->state); -cancel_delayed_work_sync (&phy->scan_work); -cancel_delayed_work_sync (&phy->mt76->mac_work);set_bit (MT76_STATE_SUSPEND , &phy->mt76->state); ieee80211_iterate_active_interfaces(hw,
Greg Kroah-Hartman
maintainerPATCH 393/403 vsock/virtio: flush works in dependency order
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit 728836ebca239810f164262b10211ef59182f811 upstream.
virtio_vsock_remove() stops the virtqueues and then flushes each work item before freeing the enclosing virtio_vsock. The current order does not account for dependencies between those items: tx_work may queue send_pkt_work, and send_pkt_work may queue rx_work.
In particular, send_pkt_work can set restart_rx and release tx_lock. The remove path can then stop the queues and flush rx_work before send_pkt_work queues it. Although the later send_pkt_work flush waits for that producer to finish, nothing waits for the newly queued rx_work, so kfree(vsock) can race with it.
KASAN reported:
BUG: KASAN: slab-use-after-free in virtio_transport_rx_work+0x487/0x4b0 Read of size 8 at addr ffff888114c2b008 by task kworker/1:1/47 Workqueue: virtio_vsock virtio_transport_rx_work Call Trace: virtio_transport_rx_work+0x487/0x4b0 process_one_work+0x688/0x1120 worker_thread+0x45b/0xd10 Allocated by task 1: virtio_vsock_probe+0xef/0x6b0 Freed by task 84: kfree+0x131/0x3c0 virtio_vsock_remove+0xd1/0x100
Flush the works in producer-to-consumer order. virtio_vsock_vqs_del() has already disabled the queue callbacks and cleared the run flags, so after tx_work and send_pkt_work are drained, no source remains that can queue rx_work after its flush.
--- net/vmw_vsock/virtio_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/vmw_vsock/virtio_transport.c +1 -1
@@ -848,10 +848,10 @@ static void virtio_vsock_remove(struct v/* Other works can be queued before 'config->del_vqs()', so we flush * all works before to free the vsock object to avoid use after free. */ -flush_work (&vsock->rx_work);flush_work (&vsock->tx_work);flush_work (&vsock->event_work);flush_work (&vsock->send_pkt_work); +flush_work (&vsock->rx_work);mutex_unlock (&the_virtio_vsock_mutex);
Greg Kroah-Hartman
maintainerPATCH 394/403 w1: ds28e17: reject an oversize length on an I2C block read
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit 169ae5e65e5aaf213b6a578f6478a9fd2e523606 upstream.
w1_f19_i2c_master_transfer() is the master_xfer for the DS28E17 1-Wire to I2C bridge. On an I2C_M_RECV_LEN read, it takes the length from the device. The downstream slave puts a length byte in buf[0]. The driver then reads that many bytes into buf[1] with w1_f19_i2c_read().
buf[0] is controlled by the device and can be 0 to 255. w1_f19_i2c_read() only rejects a zero count. The caller buffer is I2C_SMBUS_BLOCK_MAX + 2, so 34 bytes. A length above 32 makes the read run past it, up to about 222 bytes out of bounds.
The SMBus core does check buf[0] against I2C_SMBUS_BLOCK_MAX. That check runs after master_xfer returns. By then the write is already done. i2c-algo-bit rejects an oversize length before it copies, and returns -EPROTO.
Reject a length above I2C_SMBUS_BLOCK_MAX at both RECV_LEN sites, the same way i2c-algo-bit does.
--- drivers/w1/slaves/w1_ds28e17.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/w1/slaves/w1_ds28e17.c +8 -0
@@ -389,6 +389,10 @@ static int w1_f19_i2c_master_transfer(st* another simple read in that case. */ if (msgs[i+1 ].flags &I2C_M_RECV_LEN ) { +if (msgs[i+1 ].buf[0 ] >I2C_SMBUS_BLOCK_MAX ) { + i = -EPROTO ; + goto error; + } result =w1_f19_i2c_read (sl, msgs[i+1 ].addr, &(msgs[i+1 ].buf[1 ]), msgs[i+1 ].buf[0 ]);if (result <0 ) {
@@ -415,6 +419,10 @@ static int w1_f19_i2c_master_transfer(st* another simple read in that case. */ if (msgs[i].flags &I2C_M_RECV_LEN ) { +if (msgs[i].buf[0 ] >I2C_SMBUS_BLOCK_MAX ) { + i = -EPROTO ; + goto error; + } result = w1_f19_i2c_read(sl, msgs[i].addr, &(msgs[i].buf[1]),
Greg Kroah-Hartman
maintainerPATCH 395/403 xarray: honor XA_FLAGS_ACCOUNT in xas_split_alloc()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zi Yan <[email protected]>
commit 789763523fb43cdc328de5cb5dcd19240ccf90d8 upstream.
XArray operations that allocate xa_nodes, such as xas_nomem() and xas_alloc(), add __GFP_ACCOUNT when the array has XA_FLAGS_ACCOUNT set. This charges the allocated memory and avoids the workingset convergence issue described by commit 7b785645e8f13 ("mm: fix page cache convergence regression").
xas_split_alloc() does not add _GFP_ACCOUNT when XA_FLAGS_ACCOUNT is present. Fix it.
--- lib/xarray.c | 3 +++ 1 file changed, 3 insertions(+)
lib/xarray.c +3 -0
@@ -1027,6 +1027,9 @@ void xas_split_alloc(struct xa_state *xaif (xas->xa_shift +XA_CHUNK_SHIFT > order)return ; +if (xas->xa->xa_flags &XA_FLAGS_ACCOUNT ) + gfp |= __GFP_ACCOUNT; + do { unsigned int i; void *sibling = NULL;
Greg Kroah-Hartman
maintainerPATCH 396/403 tpm: tpm_i2c_nuvoton: disable IRQ on wait timeout
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 705c4ed0643366963547b2616d53165f2519c81f upstream.
i2c_nuvoton_wait_for_stat() enables the IRQ before waiting for the interrupt handler to report a status change. If the wait times out, or is interrupted before the handler runs, the function returns without balancing the enable_irq() call.
Disable the IRQ before leaving the failed wait path. Also preserve an interrupted wait's original error code instead of converting it to -ETIMEDOUT inside the helper.
--- drivers/char/tpm/tpm_i2c_nuvoton.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/char/tpm/tpm_i2c_nuvoton.c +4 -2
@@ -182,8 +182,10 @@ static int i2c_nuvoton_wait_for_stat(str timeout);if (rc >0 )return 0 ; -/* At this point we know that the SINT pin is asserted, so we -* do not need to do i2c_nuvoton_check_status */ + +disable_irq (priv->irq); +if (rc <0 ) +return rc; }else {unsigned long ten_msec, stop;bool status_valid;
Greg Kroah-Hartman
maintainerPATCH 397/403 sticon/parisc: Detect default STI graphics card for console output
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Helge Deller <[email protected]>
commit de508ece1d37cdbbbfa52f074954310f9b066b13 upstream.
If a machine has multiple graphic cards, detect the graphic card which is used to display firmware messages and use that one as the default graphic card for sticon and fbcon.
On parisc machines the default graphic card used for BCH (boot console handler, aka BIOS menu) is stored in the stable storage (equivalent to CMOS storage on x86) or in the console path in page zero. Extract that path and store it as default STI path for later comparism. Take care that the graphic card can be a GSC or a PCI card which use different path strings.
Increase max string size for default_sti_path to 32 chars as the print_pa_hwpath() function formats a hardware path using unbounded sprintf calls for up to 6 bus converter components and 1 module component (e.g., 255/255/...), which can produce a string up to 28 bytes long.
--- drivers/video/sticore.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-)
drivers/video/sticore.c +21 -1
@@ -325,7 +325,7 @@ static void sti_rom_copy(unsigned long b -static char default_sti_path[21 ]__read_mostly ; +static char default_sti_path[32 ]__read_mostly ;#ifndef MODULE static int __init sti_setup (char *str)
@@ -1148,6 +1148,26 @@ static void sti_init_roms(void)pr_info ("STI GSC/PCI core graphics driver " STI_DRIVERVERSION "\n" ); +/* +* Find default console by hardware path which is either stored in +* console entry in stable storage or alternatively from console path +* in PAGE0 used by BCH and PDC. +*/ +if (!default_sti_path[0 ]) { +struct pdc_module_path conspath; +struct device *dev =NULL ; + +if (pdc_stable_read (0x60 , &conspath,sizeof (conspath)) ==PDC_OK ) + dev =hwpath_to_device (&conspath.path); +if (!dev) + dev =hwpath_to_device (&PAGE0 ->mem_cons.dp.path); +if (dev &&dev_is_pci (dev)) +print_pci_hwpath (to_pci_dev (dev), default_sti_path); +else if (dev && !dev_is_pci (dev)) +print_pa_hwpath (to_parisc_device (dev), default_sti_path); +pr_debug ("default graphic card: %s\n" , default_sti_path); + } +/* Register drivers for native & PCI cards */ register_parisc_driver (&pa_sti_driver);WARN_ON (pci_register_driver (&pci_sti_driver));
Greg Kroah-Hartman
maintainerPATCH 398/403 signal: avoid shared siginfo namespace rewrites
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bradley Morgan <[email protected]>
commit d19cdc167e696714509e87d3f7ae765b6e164589 upstream.
send_signal_locked() rewrites sender ids for the target namespace. Group sends reuse the same siginfo, so one recipient can affect the next.
Copy the siginfo before changing it.
--- kernel/signal.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/signal.c +4 -0
@@ -1214,6 +1214,7 @@ static inline bool has_si_pid_and_uid(stint send_signal_locked (int sig,struct kernel_siginfo *info,struct task_struct *t,enum pid_type type) { +struct kernel_siginfo rewritten;/* Should SIGKILL or SIGSTOP be received by a pid namespace init? */ bool force = false;
@@ -1227,6 +1228,9 @@ int send_signal_locked(int sig, struct k/* SIGKILL and SIGSTOP is special or has ids */ struct user_namespace *t_user_ns; + rewritten = *info; + info = &rewritten; +rcu_read_lock (); t_user_ns =task_cred_xxx (t, user_ns);if (current_user_ns () != t_user_ns) {
Greg Kroah-Hartman
maintainerPATCH 399/403 smack: fix cred UAF in smack_file_send_sigiotask()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
commit fedc88e38ce979a720cd2de042578cb5df3dc8de upstream.
When inspecting the credentials of another task, objective credentials (->real_cred, accessed with __task_cred()) must always be used.
Accessing ->cred on a non-current task is forbidden unless that task is being created or destroyed; a task is allowed to change its own ->cred pointer with no synchronization, and changing ->cred should only affect the current syscall.
smack_file_send_sigiotask() was accessing both sets of credentials: First tsk->cred, then __task_cred(tsk).
Fix it, always access the objective credentials here.
I have tested that this bug can lead to a KASAN-reported UAF of struct cred in smack_file_send_sigiotask(), and that this fix prevents the race.
--- security/smack/smack_lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
security/smack/smack_lsm.c +1 -1
@@ -1988,7 +1988,7 @@ static int smack_file_send_sigiotask(str {struct smack_known **blob;struct smack_known *skp; -struct smack_known *tkp =smk_of_task (smack_cred (tsk->cred)); +struct smack_known *tkp =smk_of_task_struct_obj (tsk);const struct cred *tcred;struct file *file;int rc;
Greg Kroah-Hartman
maintainerPATCH 400/403 taskstats: fix cpumask parsing cutting off the last character
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bradley Morgan <[email protected]>
commit 1f58a5335cdd14b3fb5f2a5d3763dee1f5cba1d3 upstream.
parse() hands nla_strscpy() len as dstsize, and nla_strscpy() copies at most dstsize - 1 bytes. When the attr payload comes in without a trailing NUL, srclen == len >= dstsize and the last character of the cpumask string gets cut off. Register "0-15" and you are silently listening on "0-1", exit data for the rest never shows up.
The bug only bites when the sender doesn't NUL terminate the payload; senders that include the NUL were always fine (srclen gets decremented for the trailing NUL, so srclen < dstsize). Thats probably why this survived 20 years. And the policy is NLA_STRING, not NLA_NUL_STRING, so a payload without the trailing NUL is legit input here.
Skip the kmalloc/nla_strscpy dance entirely and use nla_strdup(), which already allocates srclen + 1 and terminates. The nla_len() bounds checks stay as they were.
--- kernel/taskstats.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
kernel/taskstats.c +1 -2
@@ -368,10 +368,9 @@ static int parse(struct nlattr *na, strureturn -E2BIG ;if (len <1 )return -EINVAL ; - data =kmalloc (len,GFP_KERNEL ); + data =nla_strdup (na,GFP_KERNEL );if (!data)return -ENOMEM ; -nla_strscpy (data, na, len); ret =cpulist_parse (data, mask);kfree (data);return ret;
Greg Kroah-Hartman
maintainerPATCH 401/403 timer: Keep debugobjects state consistent in migrate_timer_list()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Gleixner <[email protected]>
commit c793bbfc4a0a9f5a66978fc91559e9681748dbeb upstream.
When timers are migrated away from an offline CPU the debugobjects state gets corrupted. The timer is accounted as inactive on deletion, but the enqueue on the alive CPU lacks the activation call.
That used to work, but got broken when the trace point and the debug objects call got separated. That change missed to fixup migrate_timer_list().
Add the missing debug_timer_activate() invocation to fix it.
--- kernel/time/timer.c | 1 + 1 file changed, 1 insertion(+)
kernel/time/timer.c +1 -0
@@ -2673,6 +2673,7 @@ static void migrate_timer_list(struct ti timer = hlist_entry(head->first, struct timer_list, entry);detach_timer (timer, false); timer->flags = (timer->flags & ~TIMER_BASEMASK ) | cpu; +debug_timer_activate (timer);internal_add_timer (new_base, timer); } }
Greg Kroah-Hartman
maintainerPATCH 402/403 udf: Fix i_lenExtents truncation on 32-bit kernels
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
commit a5a5ed23b1340ff0f32a14a7ca8585f7c4e9b2e2 upstream.
In udf_do_extend_file() the total extent length is rounded up to a block boundary with:
iinfo->i_lenExtents = (iinfo->i_lenExtents + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1);
i_lenExtents is a __u64, but sb->s_blocksize is unsigned long. On 32-bit kernels unsigned long is 32-bit, so ~(sb->s_blocksize - 1) is a 32-bit value (e.g. 0xfffff800 for a 2 KiB block) that is zero-extended in the AND, clearing the upper 32 bits of i_lenExtents. For UDF files whose total extent length exceeds 4 GiB this truncates i_lenExtents when the file is extended, corrupting the tracked extent length.
Cast the block size to 64-bit before forming the mask. 64-bit kernels are unaffected.
--- fs/udf/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/udf/inode.c +1 -1
@@ -525,7 +525,7 @@ static int udf_do_extend_file(struct ino sb->s_blocksize - 1) & ~(sb->s_blocksize - 1)); iinfo->i_lenExtents = (iinfo->i_lenExtents + sb->s_blocksize -1 ) & - ~(sb->s_blocksize -1 ); + ~((u64 )sb->s_blocksize -1 ); } add =0 ;
Greg Kroah-Hartman
maintainerPATCH 403/403 platform/chrome: sensorhub: Fix dropped timestamp events and log spam
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tzung-Bi Shih <[email protected]>
commit 9a3f43b30373c61477d0d3ab52946c05f9492bf9 upstream.
Commit 833740a2333c ("platform/chrome: sensorhub: Bound the EC-reported sensor number") evaluated the sensor_num against the bounds limit even for timestamp events. A timestamp event typically has a sensor_num of 0xff [1], causing the driver to flag it as invalid and skip to the next event.
As a result, we'd see a flooding of "Invalid sensor number 255 from EC" warning logs and these timestamp events were being dropped.
Move the bounds-check into cros_ec_sensor_ring_process_event() and evaluate it only after standalone timestamp events have already been processed and returned early.
[1] https://crrev.com/219ca6ef82ba266da788b673ee4ad50bd3ea1285/common/motion_sense_fifo.c#427
--- drivers/platform/chrome/cros_ec_sensorhub_ring.c | 27 ++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-)
drivers/platform/chrome/cros_ec_sensorhub_ring.c +15 -12
@@ -464,6 +464,21 @@ cros_ec_sensor_ring_process_event(struct fifo_timestamp, *current_timestamp, now); + +/* +* A standalone timestamp event typically has a sensor_num of +* 0xff. Return early here to prevent it from hitting the +* bounds check below and spamming the logs. +*/ +return false; + } + +/* Skip event if sensor_num from EC is out of bounds. */ +if (in->sensor_num >= sensorhub->sensor_num) { +dev_warn_ratelimited (sensorhub->dev, +"Invalid sensor number %u from EC\n" , + in->sensor_num); +return false; }if (in->flags &MOTIONSENSE_SENSOR_FLAG_ODR ) {
@@ -491,10 +506,6 @@ cros_ec_sensor_ring_process_event(structreturn true; } -if (in->flags &MOTIONSENSE_SENSOR_FLAG_TIMESTAMP ) -/* If we just have a timestamp, skip this entry. */ -return false; -/* Regular sample */ out->sensor_id = in->sensor_num; trace_cros_ec_sensorhub_data(in->sensor_num,
@@ -879,14 +890,6 @@ static void cros_ec_sensorhub_ring_handlfor (in = sensorhub->resp->fifo_read.data, j =0 ; j < number_data; j++, in++) { -/* Skip event if sensor_num from EC is out of bounds. */ -if (in->sensor_num >= sensorhub->sensor_num) { -dev_warn_ratelimited (sensorhub->dev, -"Invalid sensor number %u from EC\n" , - in->sensor_num); -continue ; - } - if (cros_ec_sensor_ring_process_event( sensorhub, fifo_info, fifo_timestamp,