6.6.151-rc1 review
Subsystems (3)
Greg Kroah-Hartman
maintainerPATCH 142/261 selftests/mm: fix potential wild pointer access of getline due to missing init
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Gellermann <[email protected]>
commit 9f1d75a4ce04095afdb63d8e540092ff8151dacf upstream.
This is another occurrence of using getline where the code assumes that getline allocates memory to store the line, but the pointer passed to it is uninitialized and potentially a non-null pointer. This violates the Open Group Spec[1] and caused a segfault in a similar situation in selftest/clone3/clone3_set_tid. Fix it by initializing the line pointer to NULL.
The issue has been found by simply grepping through the selftest code after running into the issue in clone3_set_tid. Whether it segfaults in its current state is unknown to me. But it's good to be addressed due to defensive reasons.
--- tools/testing/selftests/mm/mlock-random-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/mm/mlock-random-test.c +1 -1
@@ -86,7 +86,7 @@ int get_proc_locked_vm_size(void)int get_proc_page_size (unsigned long addr) {FILE *smaps; -char *line; +char *line =NULL ;unsigned long mmupage_size =0 ;size_t size;
Greg Kroah-Hartman
maintainerPATCH 143/261 selftests/clone3: fix wild pointer access of getline due to missing init
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Gellermann <[email protected]>
commit 8f6f9fd93cd7a5dd607ad5cd910476dd68fff3ed upstream.
Patch series "selftests: Add missing initalization of pointer passed to getline", v2.
This patch (of 2):
Clone3_set_tid uses getline(&line, ...) in a loop to read the child's process status. The code expects that getline allocates the buffer for the line on the first loop iteration. According to the Open Group Spec[1], char *line has to be null pointer for this:
Quoted 3 lines
ssize_tgetline(char **restrict lineptr, ...);
If *lineptr is a null pointer or if the object pointed to by *lineptr is of insufficient size, an object shall be allocated as if by
malloc()
Quoted 1 line
or the object shall be reallocated as if by realloc()[...].
However, char *line is only declared, leading to an undefined value that is potentially non-null. In an example run with Musl v1.2.6, the realloc call[2] of getdelim, which implements getline, triggers a segfault:
./run_kselftest.sh --test clone3:clone3_set_tid
[ 1366.165898] kselftest: Running tests in clone3 ... [ 1367.799244] clone3_set_tid[811]: unhandled signal 11 code 0x1 at 0x0000000000000000 in libc.so[68184,3fbf69f000+4c000] [ 1367.802808] CPU: 0 UID: 0 PID: 811 Comm: clone3_set_tid Not tainted .. [ 1367.804188] epc: 0x0000003fbf6b0184 [ 1367.804188] ra : 0x0000003fbf6d4664 [ 1367.804188] sp : 0x0000003fce5f2e40 [ 1367.805314] gp : 0x0000002aaab0dfb8 [ 1367.805314] tp : 0x0000003fbf6f14a8 [ 1367.805314] t0 : 0x0000003fbf63d000
...
Looking at the realloc implementation, Musl mallocs for a null pointer memory. But for a non-null pointer, it assumes it's passed a valid pointer to the heap and tries to access its meta-data. This leads to the segfault we see:
void *realloc(void *p, size_t n)
{
if (!p) return malloc(n);
if (size_overflows(n)) return 0;
struct meta *g = get_meta(p);
...
}Fix this by properly initializing the line pointer to NULL.
--- tools/testing/selftests/clone3/clone3_set_tid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/clone3/clone3_set_tid.c +1 -1
@@ -146,7 +146,7 @@ int main(int argc, char *argv[]) {FILE *f;char buf; -char *line; +char *line =NULL ;int status;int ret =-1 ;size_t len =0 ;
Greg Kroah-Hartman
maintainerPATCH 144/261 scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ibrahim Hashimov <[email protected]>
commit 93dde0bf2f39a0f9f57fd610aa3201ce5b753433 upstream.
resp_report_zones() sizes the reply buffer from the CDB allocation length. The v3 fix rounds alloc_len up with ALIGN() before deriving the descriptor count:
rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> ilog2(RZONES_DESC_HD); arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1);
For alloc_len in 0xFFFFFFC1..0xFFFFFFFF, ALIGN() rounds up to 0x100000000, so arr_len is 4 GB. On 32-bit, kzalloc()'s size_t is 32-bit and truncates 0x100000000 to 0; kzalloc(0) returns ZERO_SIZE_PTR, which passes the !arr check, and desc = arr + 64 is then dereferenced in the loop -> out-of-bounds write / panic.
Clamp rep_max_zones to devip->nr_zones. The loop already stops at sdebug_capacity (after nr_zones zones), so a report can never hold more than nr_zones descriptors; the clamp does not change the report, it only bounds arr_len to (nr_zones + 1) * RZONES_DESC_HD, a real device property that can never reach 0x100000000.
--- drivers/scsi/scsi_debug.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/scsi/scsi_debug.c +6 -2
@@ -4469,6 +4469,7 @@ static int resp_report_zones(struct scsiu32 alloc_len, rep_opts, rep_len;bool partial;u64 lba, zs_lba; +u64 arr_len;u8 *arr =NULL , *desc;u8 *cmd = scp->cmnd;struct sdeb_zone_state *zsp =NULL ;
@@ -4490,9 +4491,12 @@ static int resp_report_zones(struct scsireturn check_condition_result; } - rep_max_zones = (alloc_len -64 ) >>ilog2 (RZONES_DESC_HD ); + rep_max_zones = (ALIGN ((u64 )alloc_len,RZONES_DESC_HD ) -RZONES_DESC_HD ) >> +ilog2 (RZONES_DESC_HD ); + rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones); + arr_len = (u64 )RZONES_DESC_HD * (rep_max_zones +1 ); - arr =kzalloc (alloc_len,GFP_ATOMIC | __GFP_NOWARN); + arr =kzalloc (arr_len,GFP_ATOMIC | __GFP_NOWARN);if (!arr) {mk_sense_buffer (scp,ILLEGAL_REQUEST ,INSUFF_RES_ASC ,INSUFF_RES_ASCQ );
Greg Kroah-Hartman
maintainerPATCH 145/261 sctp: reject stale cookies with mismatched verification tags
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuxiang Yang <[email protected]>
commit 9d8da8e0a9bce4a340af60dd0446bc7eb8d07587 upstream.
sctp_unpack_cookie() skips cookie expiration checks whenever an association already exists. This is broader than the exception in RFC 9260 Section 5.2.4.
For an existing association, Section 5.2.4 permits an expired State Cookie only when both Verification Tags in the cookie match the current association. Otherwise, the packet SHOULD be discarded and a Stale Cookie ERROR MUST be sent.
The broad check lets an expired Action A restart cookie reach sctp_sf_do_dupcook_a(). In a runtime test with the default 60 second cookie lifetime, replaying such a cookie after 65 seconds returned a COOKIE-ACK and restarted the association.
Check cookie expiration unless both Verification Tags match. This preserves the Action D exception for a lost COOKIE ACK while rejecting expired cookies in all other cases.
--- net/sctp/sm_make_chunk.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
net/sctp/sm_make_chunk.c +7 -4
@@ -1821,9 +1821,9 @@ no_hmac: goto fail; } -/* Check to see if the cookie is stale. If there is already -* an association, there is no need to check cookie's expiration -* for init collision case of lost COOKIE ACK. +/* Check to see if the cookie is stale. RFC 9260 Section 5.2.4 +* exempts an expired cookie only when both Verification Tags match +* the current association. * If skb has been timestamped, then use the stamp, otherwise * use current time. This introduces a small possibility that * a cookie may be considered expired, but this would only slow
@@ -1834,7 +1834,10 @@ no_hmac:else kt =ktime_get_real (); -if (!asoc &&ktime_before (bear_cookie->expiration, kt)) { +if ((!asoc || + asoc->c.my_vtag != bear_cookie->my_vtag || + asoc->c.peer_vtag != bear_cookie->peer_vtag) && +ktime_before (bear_cookie->expiration, kt)) {suseconds_t usecs =ktime_to_us (ktime_sub (kt, bear_cookie->expiration));__be32 n =htonl (usecs);
Greg Kroah-Hartman
maintainerPATCH 146/261 sctp: prevent peer transport count overflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asim Viladi Oglu Manizada <[email protected]>
commit bd0e9289e2642f6a5c54faad304ce0f41e926d22 upstream.
sctp_assoc_add_peer() increments the association's 16-bit transport_count for every new unique peer. Adding the 65,536th transport wraps the count to zero.
SCTP sock_diag uses transport_count to reserve the INET_DIAG_PEERS payload, then copies one sockaddr_storage for every entry in transport_addr_list. After the wrap, a diagnostic dump reserves an empty payload and writes 8 MiB of peer addresses past the skb tail.
Reject a new unique peer when transport_count has reached U16_MAX. Perform the check after the existing-peer lookup so a duplicate address continues to return its existing transport at the limit.
--- net/sctp/associola.c | 3 +++ 1 file changed, 3 insertions(+)
net/sctp/associola.c +3 -0
@@ -614,6 +614,9 @@ struct sctp_transport *sctp_assoc_add_pereturn peer; } +if (asoc->peer.transport_count ==U16_MAX ) +return NULL ; + peer =sctp_transport_new (asoc->base.net, addr, gfp);if (!peer)return NULL ;
Greg Kroah-Hartman
maintainerPATCH 147/261 hwmon: (npcm750-pwm-fan): stop fan timer on device detach
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongyan Xu <[email protected]>
commit f27f6976ea269219c1259a7c2f8c6dfe782540a3 upstream.
When a fan tach channel is present, npcm7xx_pwm_fan_probe() starts fan_timer. The timer callback polls tach state and rearms the timer, but the driver has no remove callback or devm cleanup action to stop it. On device detach, the devm-managed driver data and I/O mappings can be released while the timer is still pending or running.
Register a devm cleanup action before starting the timer and shut the timer down synchronously from that action.
This issue was found by a static analysis tool.
--- drivers/hwmon/npcm750-pwm-fan.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/hwmon/npcm750-pwm-fan.c +11 -0
@@ -360,6 +360,11 @@ static void npcm7xx_fan_polling(struct tadd_timer (&data->fan_timer); } +static void npcm7xx_fan_cleanup (void *timer) +{ +timer_shutdown_sync (timer); +} +static inline void npcm7xx_fan_compute (struct npcm7xx_pwm_fan_data *data,u8 fan,u8 cmp,u8 fan_id,u8 flag_int,u8 flag_mode,u8 flag_clear)
@@ -1003,6 +1008,12 @@ static int npcm7xx_pwm_fan_probe(structmsecs_to_jiffies (NPCM7XX_FAN_POLL_TIMER_200MS );timer_setup (&data->fan_timer, npcm7xx_fan_polling,0 ); + ret =devm_add_action_or_reset (dev, + npcm7xx_fan_cleanup, + &data->fan_timer); +if (ret) +return ret; +add_timer (&data->fan_timer);break ; }
Greg Kroah-Hartman
maintainerPATCH 148/261 hwmon: (pmbus/core) notify on the hwmon device, not the i2c client
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Jardin <[email protected]>
commit a64a7e8a0b012ba81b0eadbd7afc84ab0dbfd70c upstream.
pmbus_notify() calls sysfs_notify() and kobject_uevent() on the i2c client's kobject, but the alarm attributes live on the hwmon class device registered by pmbus_do_probe(). Notifying the parent i2c device is a no-op for both poll(POLLPRI) waiters and udev listeners: the named attribute does not exist on that kobject.
Notify the hwmon device instead, so poll() wakes up and "change" uevents fire on the inX_alarm/tempX_alarm attributes when SMBALERT# reports a fault.
--- drivers/hwmon/pmbus/pmbus_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/hwmon/pmbus/pmbus_core.c +3 -2
@@ -2803,8 +2803,9 @@ static void pmbus_notify(struct pmbus_daif (reg == sreg && page == spage && (smask & flags)) {dev_dbg (data->dev,"sysfs notify: %s" , da->attr.name); -sysfs_notify (&data->dev->kobj,NULL , da->attr.name); -kobject_uevent (&data->dev->kobj,KOBJ_CHANGE ); +sysfs_notify (&data->hwmon_dev->kobj,NULL , + da->attr.name); +kobject_uevent (&data->hwmon_dev->kobj,KOBJ_CHANGE ); flags &= ~smask; }
Greg Kroah-Hartman
maintainerPATCH 149/261 i2c: amd-mp2: Unregister callback on adapter add failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 82048795242f04275a3f49ffc66ad851b6120954 upstream.
amd_mp2_register_cb() stores the platform I2C context in the MP2 PCI driver's callback table before the adapter is registered. If i2c_add_adapter() fails, probe returns and devres frees the context, but the PCI driver can still dereference the stale pointer from its IRQ and system-sleep callbacks.
Unregister the callback before returning the adapter registration error.
--- drivers/i2c/busses/i2c-amd-mp2-plat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-amd-mp2-plat.c +3 -1
@@ -316,8 +316,10 @@ static int i2c_amd_probe(struct platformamd_mp2_pm_runtime_put (mp2_dev); -if (ret <0 ) +if (ret <0 ) {dev_err (&pdev->dev,"i2c add adapter failed = %d\n" , ret); +amd_mp2_unregister_cb (&i2c_dev->common); + }return ret; }
Greg Kroah-Hartman
maintainerPATCH 150/261 gpio: pca953x: fix cache_only and IRQ state on restore_context() failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: bui duc phuc <[email protected]>
commit d233087c19f6607ef926ac3f47d776e2406ffd1f upstream.
When pca953x_restore_context() fails, cache_only is left disabled and the IRQ left enabled, even though register synchronization may not have completed successfully. Restore cache_only and disable the IRQ again on failure, matching the state set by pca953x_save_context().
--- drivers/gpio/gpio-pca953x.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
drivers/gpio/gpio-pca953x.c +13 -2
@@ -1258,9 +1258,20 @@ static int pca953x_restore_context(strucregcache_mark_dirty (chip->regmap); ret =pca953x_regcache_sync (chip);if (ret) -return ret; + goto err; -return regcache_sync (chip->regmap); + ret =regcache_sync (chip->regmap); +if (ret) + goto err; + +return 0 ; + +err: +if (chip->client->irq >0 ) +disable_irq (chip->client->irq); +regcache_cache_only (chip->regmap, true); + +return ret; }static void pca953x_save_context (struct pca953x_chip *chip)
Greg Kroah-Hartman
maintainerPATCH 151/261 cpufreq: powernow-k8: Fix possible memory leak in powernowk8_cpu_init()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit d5f8e5f6040d052d44fcbf4f31dd35145c0c8d7d upstream.
The memory allocated for data->powernow_table inside powernow_k8_cpu_init_acpi() or find_psb_table() is not freed in one of the error paths in powernowk8_cpu_init(). Fix that by adding a kfree().
--- drivers/cpufreq/powernow-k8.c | 1 + 1 file changed, 1 insertion(+)
drivers/cpufreq/powernow-k8.c +1 -0
@@ -1083,6 +1083,7 @@ static int powernowk8_cpu_init(struct cp err_out_exit_acpi:powernow_k8_cpu_exit_acpi (data); +kfree (data->powernow_table); err_out:kfree (data);
Greg Kroah-Hartman
maintainerPATCH 152/261 power: supply: bq25890: fix the -10 C NTC lookup entry
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit 160a783aa65b74782bc17cb874af1a6d3f5fba3c upstream.
The TSPCT lookup table is monotonically decreasing except for ADC code 121, where the sequence reads -9.0 C, -1.0 C, -12.0 C. This makes the reported battery temperature jump upward by eight degrees for one code and then downward by eleven degrees for the next code.
The entry is a missing zero: use -10.0 C so the sequence remains monotonic between -9.0 C and -12.0 C.
--- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/bq25890_charger.c +1 -1
@@ -320,7 +320,7 @@ static const u32 bq25890_tspct_tbl[] = {145 ,140 ,130 ,120 ,115 ,110 ,100 ,90 ,80 ,70 ,60 ,50 ,40 ,30 ,20 ,10 ,0 ,-10 ,-20 ,-30 ,-40 ,-60 ,-70 ,-80 , - -90, -10, -120, -140, -150, -170, -190, -210, + -90, -100, -120, -140, -150, -170, -190, -210, };#define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl)
Greg Kroah-Hartman
maintainerPATCH 153/261 s390/qeth: Check CAP_NET_ADMIN for private ioctls
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aswin Karuvally <[email protected]>
commit d211028bac1bd0fff0026bfa2a8328e5b78cd0e6 upstream.
Gate the SIOCDEVPRIVATE ioctl commands SIOC_QETH_ADP_SET_SNMP_CONTROL, SIOC_QETH_GET_CARD_TYPE and SIOC_QETH_QUERY_OAT with CAP_NET_ADMIN capable check to ensure unprivileged users cannot invoke them.
--- drivers/s390/net/qeth_core_main.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/s390/net/qeth_core_main.c +3 -0
@@ -6526,6 +6526,9 @@ int qeth_siocdevprivate(struct net_devicstruct qeth_card *card = dev->ml_priv;int rc =0 ; +if (!capable (CAP_NET_ADMIN )) +return -EPERM ; +switch (cmd) {case SIOC_QETH_ADP_SET_SNMP_CONTROL : rc =qeth_snmp_command (card, data);
Greg Kroah-Hartman
maintainerPATCH 154/261 s390/dasd: Fix potential NULL pointer dereference
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Höppner <[email protected]>
commit 9973026f572db6b67570cadc30942f3014e41079 upstream.
dasd_release_space() checks the implementation of the is_ese() discipline function before calling it to determine if a given device is an ESE DASD.
The current usage of the logical AND operator will lead to a NULL pointer dereference as the function is called even if the function pointer is NULL.
Fix this by using the logical OR operator.
--- drivers/s390/block/dasd_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/s390/block/dasd_ioctl.c +1 -1
@@ -330,7 +330,7 @@ out_err:static int dasd_release_space (struct dasd_device *device,struct format_data_t *rdata) { -if (!device->discipline->is_ese && !device->discipline->is_ese (device)) +if (!device->discipline->is_ese || !device->discipline->is_ese (device))return -ENOTSUPP ;if (!device->discipline->release_space)return -ENOTSUPP ;
Greg Kroah-Hartman
maintainerPATCH 155/261 s390/dasd: Fix undersized format-check buffer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Stefan Haberland <[email protected]>
commit 7f40b346462f563a0d6e841a77b5163d2a882a04 upstream.
fmt_buffer_size in dasd_eckd_check_device_format() is declared as int, even though one of the multiplicands, sizeof(struct eckd_count), is a size_t. The expression
trkcount * rpt_max * sizeof(struct eckd_count)
is therefore correctly evaluated at 64-bit width, but the result is silently truncated when it is stored back into the 32-bit fmt_buffer_size variable. For a sufficiently large track range (start_unit/stop_unit are caller-controlled) this truncation yields a buffer size far smaller than the number of tracks actually requested. kzalloc() then succeeds with an undersized allocation, while the subsequent channel program build still operates on the untruncated track count and writes past the end of that buffer.
Compute the buffer size with check_mul_overflow() and keep it in a size_t, so that a value that no longer fits results in -EINVAL instead of a silently truncated allocation size.
--- drivers/s390/block/dasd_eckd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
drivers/s390/block/dasd_eckd.c +7 -4
@@ -23,6 +23,7 @@#include <linux/seq_file.h> #include <linux/uaccess.h> #include <linux/io.h> +#include <linux/overflow.h> #include <asm/css_chars.h> #include <asm/debug.h>
@@ -3489,11 +3490,11 @@ static int dasd_eckd_check_device_format {struct dasd_eckd_private *private = base->private;struct eckd_count *fmt_buffer; -struct irb irb; +size_t fmt_buffer_size; +unsigned int trkcount;int rpt_max, rpt_exp; -int fmt_buffer_size; +struct irb irb;int trk_per_cyl; -int trkcount;int tpm =0 ;int rc;
@@ -3504,7 +3505,9 @@ static int dasd_eckd_check_device_format rpt_exp =recs_per_track (&private->rdc_data,0 , cdata->expect.blksize); trkcount = cdata->expect.stop_unit - cdata->expect.start_unit +1 ; - fmt_buffer_size = trkcount * rpt_max *sizeof (struct eckd_count ); +if (check_mul_overflow (trkcount, rpt_max, &fmt_buffer_size) || +check_mul_overflow (fmt_buffer_size,sizeof (struct eckd_count ), &fmt_buffer_size)) +return -EINVAL ; fmt_buffer =kzalloc (fmt_buffer_size,GFP_KERNEL |GFP_DMA );if (!fmt_buffer)
Greg Kroah-Hartman
maintainerPATCH 156/261 s390/zcrypt: Fix wrong domain value verification with EP11 CPRBs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harald Freudenberger <[email protected]>
commit 983279d7f86ade73db86f886e09172dd567031b5 upstream.
There is a wrong upper limit check for the domain value when an EP11 CPRB is processed for sending to a crypto card. This check is only active on custom device nodes but may lead to access heap memory behind perms->adm when an administrative CPRB is sent. Add correct limit (AP_DOMAINS = 256) checking to fix this.
--- drivers/s390/crypto/zcrypt_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/s390/crypto/zcrypt_api.c +1 -1
@@ -1048,7 +1048,7 @@ static long _zcrypt_send_ep11_cprb(boolif (rc) goto out_free; -if (perms != &ap_perms && domain <AUTOSEL_DOM ) { +if (perms != &ap_perms && domain <AP_DOMAINS ) {if (ap_msg.flags &AP_MSG_FLAG_ADMIN ) {if (!test_bit_inv (domain, perms->adm)) { rc = -ENODEV ;
Greg Kroah-Hartman
maintainerPATCH 157/261 s390/zcrypt: Validate length for CCA AES cipher key requests
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Holger Dengler <[email protected]>
commit 06afe425d5283b9764303de47f554da5a808ce8a upstream.
cca_cipher2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block.
--- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/s390/crypto/zcrypt_ccamisc.c +3 -0
@@ -1232,6 +1232,9 @@ int cca_cipher2protkey(u16 cardnr, u16 d }__packed * prepparm;int keytoklen = ((struct cipherkeytoken *)ckey)->len; +if (keytoklen >PARMBSIZE -sizeof (struct aureqparm )) +return -EINVAL ; +/* get already prepared memory for 2 cprbs with param block each */ rc =alloc_and_prep_cprbmem (PARMBSIZE , &mem, &preqcblk, &prepcblk);if (rc)
Greg Kroah-Hartman
maintainerPATCH 158/261 s390/zcrypt: Validate length for CCA ECC private key requests
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Holger Dengler <[email protected]>
commit a9ae0f6dd45c3ccc1d69363f7aea8af179122730 upstream.
cca_ecc2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block.
--- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/s390/crypto/zcrypt_ccamisc.c +3 -0
@@ -1402,6 +1402,9 @@ int cca_ecc2protkey(u16 cardnr, u16 doma }__packed * prepparm;int keylen = ((struct eccprivkeytoken *)key)->len; +if (keylen >PARMBSIZE -sizeof (struct aureqparm )) +return -EINVAL ; +/* get already prepared memory for 2 cprbs with param block each */ rc =alloc_and_prep_cprbmem (PARMBSIZE , &mem, &preqcblk, &prepcblk);if (rc)
Greg Kroah-Hartman
maintainerPATCH 159/261 phy: zynqmp: fix L0_TM_DISABLE_SCRAMBLE_ENCODER mask
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nava kishore Manne <[email protected]>
commit 6cb22477929489a412df8d153e550e77a012e701 upstream.
The L0_TX_DIG_61 register bit 2 is a reserved read-only field. The previous mask value 0x0f incorrectly included bit 2, causing unintended writes to a reserved bit on every scrambler bypass operation.
Correct the mask to (BIT(3) | GENMASK(1, 0)) to cover only the valid scramble bypass control bits.
--- drivers/phy/xilinx/phy-zynqmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/phy/xilinx/phy-zynqmp.c +1 -1
@@ -52,7 +52,7 @@#define L0_TM_DIG_6 0x106c#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f#define L0_TX_DIG_61 0x00f4 -#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f +#define L0_TM_DISABLE_SCRAMBLE_ENCODER (BIT(3) | GENMASK(1, 0))/* PLL Test Mode register parameters */ #define L0_TM_PLL_DIG_37 0x2094
Greg Kroah-Hartman
maintainerPATCH 160/261 phy: zynqmp: use read-modify-write for SERDES scrambler bypass
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nava kishore Manne <[email protected]>
commit 21e0749f931702765b9d52d05740092bc87fcd8d upstream.
xpsgtr_bypass_scrambler_8b10b() used xpsgtr_write_phy() which performs a full register write, silently clearing any bits beyond the intended bypass control fields.
Switch to xpsgtr_clr_set_phy() with clr=mask, set=mask to set only the bypass bits while preserving the remaining bits in each register.
--- drivers/phy/xilinx/phy-zynqmp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +6 -2
@@ -490,8 +490,12 @@ static void xpsgtr_lane_set_protocol(str/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ static void xpsgtr_bypass_scrambler_8b10b (struct xpsgtr_phy *gtr_phy) { -xpsgtr_write_phy (gtr_phy,L0_TM_DIG_6 ,L0_TM_DIS_DESCRAMBLE_DECODER ); -xpsgtr_write_phy (gtr_phy,L0_TX_DIG_61 ,L0_TM_DISABLE_SCRAMBLE_ENCODER ); +xpsgtr_clr_set_phy (gtr_phy,L0_TM_DIG_6 , +L0_TM_DIS_DESCRAMBLE_DECODER , +L0_TM_DIS_DESCRAMBLE_DECODER ); +xpsgtr_clr_set_phy (gtr_phy,L0_TX_DIG_61 , +L0_TM_DISABLE_SCRAMBLE_ENCODER , +L0_TM_DISABLE_SCRAMBLE_ENCODER ); }/* DP-specific initialization. */
Greg Kroah-Hartman
maintainerPATCH 161/261 phy: zynqmp: keep SERDES scrambler and 8b/10b enabled for USB
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nava kishore Manne <[email protected]>
commit 7eb61caf45607e1e1270f51f8f93f0ded53146da upstream.
USB Gen1 requires scrambling and 8b/10b encoding to be performed in the physical layer. Do not bypass PHY-side scrambler or encoder/decoder for USB operation, as mandated by the USB 3.x specification.
Scrambler and 8b/10b bypass remain restricted to SATA and SGMII modes, where encoding is handled in the controller.
--- drivers/phy/xilinx/phy-zynqmp.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +29 -10
@@ -487,15 +487,30 @@ static void xpsgtr_lane_set_protocol(str } } -/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ -static void xpsgtr_bypass_scrambler_8b10b (struct xpsgtr_phy *gtr_phy) +/** +* xpsgtr_bypass_scrambler_8b10b - Configure scrambler/encoder behavior +* @gtr_phy: pointer to lane context +* @bypass: true to enable scrambler/encoder bypass (SATA/SGMII), +* false to disable scrambler/encoder bypass (USB3) +* +* Uses RMW to preserve reserved and unrelated register fields. +*/ +static void xpsgtr_bypass_scrambler_8b10b (struct xpsgtr_phy *gtr_phy, +bool bypass) { -xpsgtr_clr_set_phy (gtr_phy,L0_TM_DIG_6 , -L0_TM_DIS_DESCRAMBLE_DECODER , -L0_TM_DIS_DESCRAMBLE_DECODER ); -xpsgtr_clr_set_phy (gtr_phy,L0_TX_DIG_61 , -L0_TM_DISABLE_SCRAMBLE_ENCODER , -L0_TM_DISABLE_SCRAMBLE_ENCODER ); +if (bypass) { +xpsgtr_clr_set_phy (gtr_phy,L0_TM_DIG_6 , +L0_TM_DIS_DESCRAMBLE_DECODER , +L0_TM_DIS_DESCRAMBLE_DECODER ); +xpsgtr_clr_set_phy (gtr_phy,L0_TX_DIG_61 , +L0_TM_DISABLE_SCRAMBLE_ENCODER , +L0_TM_DISABLE_SCRAMBLE_ENCODER ); + }else { +xpsgtr_clr_set_phy (gtr_phy,L0_TM_DIG_6 , +L0_TM_DIS_DESCRAMBLE_DECODER ,0 ); +xpsgtr_clr_set_phy (gtr_phy,L0_TX_DIG_61 , +L0_TM_DISABLE_SCRAMBLE_ENCODER ,0 ); + } }/* DP-specific initialization. */
@@ -516,7 +531,7 @@ static void xpsgtr_phy_init_sata(struct {struct xpsgtr_dev *gtr_dev = gtr_phy->dev; -xpsgtr_bypass_scrambler_8b10b (gtr_phy); +xpsgtr_bypass_scrambler_8b10b (gtr_phy, true);writel (gtr_phy->lane, gtr_dev->siou +SATA_CONTROL_OFFSET ); }
@@ -532,7 +547,7 @@ static void xpsgtr_phy_init_sgmii(structxpsgtr_clr_set (gtr_dev,TX_PROT_BUS_WIDTH , mask, val);xpsgtr_clr_set (gtr_dev,RX_PROT_BUS_WIDTH , mask, val); -xpsgtr_bypass_scrambler_8b10b (gtr_phy); +xpsgtr_bypass_scrambler_8b10b (gtr_phy, true); }/* Configure TX de-emphasis and margining for DP. */
@@ -693,6 +708,10 @@ static int xpsgtr_phy_init(struct phy *pcase ICM_PROTOCOL_SGMII :xpsgtr_phy_init_sgmii (gtr_phy);break ; + +case ICM_PROTOCOL_USB : +xpsgtr_bypass_scrambler_8b10b (gtr_phy, false); +break ; } goto out;
Greg Kroah-Hartman
maintainerPATCH 162/261 net: openvswitch: fix potential UAF on meter attach failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Maximets <[email protected]>
commit a58a2b0ce354df531ebc71fc870058c2feb59f6b upstream.
While attaching a newly created meter attach_meter() function makes the new meter visible to other CPUs but can still fail afterwards. On failure, it detaches the meter back and returns an error.
However, this is an unexpected behavior for the ovs_meter_cmd_set() that uses a plain kfree(meter) on attach failure without waiting for RCU readers to stop using it, assuming it was never visible.
This is never a problem for ovs-vswitchd as it always creates meters before creating any flows that use them. But the UAF can be triggered with a custom application using uAPI:
BUG: KASAN: slab-use-after-free in ovs_meter_execute (net/openvswitch/meter.c:653) Read of size 8 at addr ffff88810d152650 by task meter/2508 Call Trace: ovs_meter_execute (net/openvswitch/meter.c:653) do_execute_actions (net/openvswitch/actions.c:1407) ovs_execute_actions (net/openvswitch/actions.c:1584) ovs_packet_cmd_execute (net/openvswitch/datapath.c:703) ... netlink_sendmsg (af_netlink.c:1900) Allocated by task 2519: __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415) ovs_meter_cmd_set (net/openvswitch/meter.c:422) ... netlink_sendmsg (af_netlink.c:1900) Freed by task 2519: kfree (mm/slub.c:2705 mm/slub.c:6405 mm/slub.c:6720) ovs_meter_cmd_set (net/openvswitch/meter.c:479) ... netlink_sendmsg (af_netlink.c:1900)
Fix that by making sure attach_meter() doesn't make the meter visible until all the checks are done and the function can't fail anymore.
This also makes sure the "hash" value is calculated after the potential re-sizing of the table.
Reported by Trend Micro's Zero Day Initiative as ZDI-CAN-31642.
--- net/openvswitch/meter.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-)
net/openvswitch/meter.c +19 -14
@@ -133,18 +133,10 @@ static void dp_meter_instance_remove(strstatic int attach_meter (struct dp_meter_table *tbl,struct dp_meter *meter) { -struct dp_meter_instance *ti =rcu_dereference_ovsl (tbl->ti); -u32 hash =meter_hash (ti, meter->id); +struct dp_meter_instance *ti; +u32 hash;int err; -/* In generally, slots selected should be empty, because -* OvS uses id-pool to fetch a available id. -*/ -if (unlikely (rcu_dereference_ovsl (ti->dp_meters[hash]))) -return -EBUSY ; - -dp_meter_instance_insert (ti, meter); -/* That function is thread-safe. */ tbl->count++;if (tbl->count >= tbl->max_meters_allowed) {
@@ -152,16 +144,29 @@ static int attach_meter(struct dp_meter_ goto attach_err; } -if (tbl->count >= ti->n_meters && -dp_meter_instance_realloc (tbl, ti->n_meters *2 )) { - err = -ENOMEM ; + ti =rcu_dereference_ovsl (tbl->ti); +if (tbl->count >= ti->n_meters) { + err =dp_meter_instance_realloc (tbl, ti->n_meters *2 ); +if (err) + goto attach_err; + + ti =rcu_dereference_ovsl (tbl->ti); + } + + hash =meter_hash (ti, meter->id); + +/* In general, selected slots should be empty, because +* OvS uses id-pool to fetch available ids. +*/ +if (unlikely (rcu_dereference_ovsl (ti->dp_meters[hash]))) { + err = -EBUSY ; goto attach_err; } +dp_meter_instance_insert (ti, meter);return 0 ; attach_err: -dp_meter_instance_remove (ti, meter); tbl->count--;return err; }
Greg Kroah-Hartman
maintainerPATCH 163/261 net: openvswitch: fix skb leak on flow key update failure during recirculation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Maximets <[email protected]>
commit e1cf066244dad576221b7123a0e5005967f25a20 upstream.
do_execute_actions() returns right away when execute_recirc() fails on the last action as it assumes this function always takes ownership of the skb when 'last' is true. But when the flow key update fails, the function doesn't free the skb and it ends up leaked.
This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless.
Reported by Sashiko.
--- net/openvswitch/actions.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
net/openvswitch/actions.c +9 -1
@@ -1120,6 +1120,10 @@ static int execute_masked_set_action(strreturn err; } +/* When 'last' is true, recirc() should always consume the 'skb'. +* Otherwise, recirc() should keep 'skb' intact regardless what +* actions are executed on recirculation. +*/ static int execute_recirc (struct datapath *dp,struct sk_buff *skb,struct sw_flow_key *key,const struct nlattr *a,bool last)
@@ -1130,8 +1134,12 @@ static int execute_recirc(struct datapatint err; err =ovs_flow_key_update (skb, key); -if (err) +if (err) { +if (last) +ovs_kfree_skb_reason (skb, +OVS_DROP_ACTION_ERROR );return err; + } }BUG_ON (!is_flow_key_valid (key));
Greg Kroah-Hartman
maintainerPATCH 164/261 net: openvswitch: fix skb leak on flow key update failure during ct
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Maximets <[email protected]>
commit bc62e843bc48f933da765ce47079fd992e535794 upstream.
ovs_ct_execute() always steals or frees the skb on failure while ovs_flow_key_update() does not. So, if it fails and we return right away, the skb ends up leaked.
Fix that by breaking instead and letting the common error handling code at the bottom of the loop to free the skb properly.
This is a very unlikely scenario as it requires the packet to become unparseable by applying a set of actions on a previously parseable skb, but should be fixed nevertheless.
Reported by Sashiko.
--- net/openvswitch/actions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/openvswitch/actions.c +1 -1
@@ -1354,7 +1354,7 @@ static int do_execute_actions(struct datif (!is_flow_key_valid (key)) { err =ovs_flow_key_update (skb, key);if (err) -return err; +break ; } err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key,
Greg Kroah-Hartman
maintainerPATCH 165/261 ice: wait for reset completion in ice_resume()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaron Ma <[email protected]>
commit c2816d613f388814d27bc9fd6dbd931a88056e19 upstream.
ice_resume() schedules an asynchronous PF reset and returns immediately. The reset runs later in ice_service_task(). If userspace tries to bring up the net device before the reset finishes, ice_open() fails with -EBUSY:
ice_resume()
ice_schedule_reset() # sets ICE_PFR_REQ, returns
...
ice_open()
ice_is_reset_in_progress() # ICE_PFR_REQ still set, -EBUSY
...
ice_service_task()
ice_do_reset()
ice_rebuild() # clears ICE_PFR_REQ, too late
Reproduced on E800 series NICs during suspend/resume with irdma enabled, where the aux device probe widens the race window.
ice 0000:81:00.0: can't open net device while reset is in progress
Add a best-effort wait (10s timeout, matching ice_devlink_info_get()) for the reset to complete before returning from ice_resume(). In practice the reset completes in ~300ms.
--- drivers/net/ethernet/intel/ice/ice_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/net/ethernet/intel/ice/ice_main.c +10 -0
@@ -5491,6 +5491,16 @@ static int __maybe_unused ice_resume(str/* Restart the service task */ mod_timer (&pf->serv_tmr,round_jiffies (jiffies + pf->serv_tmr_period)); +/* Best-effort wait for the scheduled reset to finish so that the +* device is operational before returning. Without this, userspace +* (e.g. NetworkManager) may try to open the net device while the +* asynchronous reset is still in progress, hitting -EBUSY. +*/ + ret =ice_wait_for_reset (pf,secs_to_jiffies (10 )); +if (ret) +dev_err (dev,"Wait for reset timed out (10s) during resume: %d\n" , + ret); + return 0; }#endif /* CONFIG_PM */
Greg Kroah-Hartman
maintainerPATCH 166/261 ice: fix memory leak in ice_lbtest_prepare_rings()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
commit 3a9de5590da4ffd9e9c541c4c4d492aa2b54cf6e upstream.
ice_lbtest_prepare_rings() frees Rx rings only when ice_vsi_start_all_rx_rings() fails. If ice_vsi_setup_rx_rings() fails after allocating some descriptors, or if ice_vsi_cfg_lan() fails after the Rx rings were prepared, the function reaches the Tx cleanup path without releasing the initialized Rx resources.
Fix this by adding separate unwind paths for Rx setup failure and LAN configuration failure. The Rx setup failure path releases the partially prepared Rx rings before freeing Tx rings, while later failures first undo the LAN Tx configuration and then release the Rx rings in reverse setup order.
The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. 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 an Intel E800 Series adapter available to run the ethtool offline loopback selftest, no runtime testing was able to be performed.
--- drivers/net/ethernet/intel/ice/ice_ethtool.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/net/ethernet/intel/ice/ice_ethtool.c +5 -5
@@ -659,18 +659,18 @@ static int ice_lbtest_prepare_rings(stru status =ice_vsi_cfg_lan (vsi);if (status) - goto err_setup_rx_ring; + goto err_cfg_lan; status =ice_vsi_start_all_rx_rings (vsi);if (status) - goto err_start_rx_ring; + goto err_cfg_lan;return 0 ; -err_start_rx_ring: -ice_vsi_free_rx_rings (vsi); -err_setup_rx_ring: +err_cfg_lan:ice_vsi_stop_lan_tx_rings (vsi,ICE_NO_RESET ,0 ); +err_setup_rx_ring: +ice_vsi_free_rx_rings (vsi); err_setup_tx_ring:ice_vsi_free_tx_rings (vsi);
Greg Kroah-Hartman
maintainerPATCH 167/261 i2c: jz4780: Cache host clock rate at probe to prevent CCF prepare_lock deadlock
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: H. Nikolaus Schaller <[email protected]>
commit d99607c888f26e8a4e9fe9772860cef4aff86bb4 upstream.
Fix a severe AB/BA deadlock between the Common Clock Framework (CCF) and the I2C adapter lock, which triggers when an I2C-controlled clock generator client (like the Si5351) is registered or modified under the CCF.
During an i2c client clock (generator) frequency change, the CCF acquires its global 'prepare_lock' mutex and the driver calls i2c_transfer() to update the client's chip registers, stalling for the adapter's I2C bus lock.
Concurrently, an independent, parallel transfer on the same bus (e.g., a GPIO expander handling LEDs) can hold the I2C adapter lock. Inside this parallel transfer path, jz4780_i2c_set_speed() calls clk_get_rate() on the host controller's input clock to calculate bus timings. This call attempts to acquire the blocked CCF 'prepare_lock', creating a circular dependency that freezes the system.
The jz4780 host controller clock itself is static and never changes at runtime.
However, calling clk_get_rate() inside the active transfer path introduces an unnecessary dependency on the CCF internal locks.
Eliminate this synchronous clk_get_rate() call from the active transfer path by caching the static host peripheral clock rate once - inside the private jz4780_i2c structure during jz4780_i2c_probe(). Update jz4780_i2c_set_speed() to use this cached value, safely decoupling active I2C transactions from the CCF internal locks without any risk of stale timings.
Assisted-by web based Google AI (pinpointing the bug and writing the message).
--- drivers/i2c/busses/i2c-jz4780.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-jz4780.c +4 -1
@@ -141,6 +141,7 @@ struct jz4780_i2c {void __iomem *iomem;int irq;struct clk *clk; +unsigned long clk_rate_khz;struct i2c_adapter adap;const struct ingenic_i2c_config *cdata;
@@ -246,7 +247,7 @@ static int jz4780_i2c_set_target(structstatic int jz4780_i2c_set_speed (struct jz4780_i2c *i2c) { -int dev_clk_khz =clk_get_rate (i2c->clk) /1000 ; +int dev_clk_khz = i2c->clk_rate_khz;int cnt_high =0 ;/* HIGH period count of the SCL clock */ int cnt_low =0 ;/* LOW period count of the SCL clock */ int cnt_period =0 ;/* period count of the SCL clock */
@@ -800,6 +801,8 @@ static int jz4780_i2c_probe(struct platfif (ret)return ret; + i2c->clk_rate_khz =clk_get_rate (i2c->clk) /1000 ; + ret =of_property_read_u32 (pdev->dev.of_node,"clock-frequency" , &clk_freq);if (ret) {
Greg Kroah-Hartman
maintainerPATCH 168/261 i2c: imx: Fix slave registration race and error handling
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liem <[email protected]>
commit d64ec362c369bbc33833f7936d5f3a706b0d5c45 upstream.
In i2c_imx_reg_slave(), the slave pointer was assigned before pm_runtime_resume_and_get(). If pm_runtime_resume_and_get() failed, the error path returned without clearing i2c_imx->slave, leaving it non-NULL and causing all subsequent registration attempts to fail with -EBUSY.
Additionally, because this driver uses a shared IRQ, the interrupt handler i2c_imx_isr() can execute concurrently and, after acquiring slave_lock, dereference i2c_imx->slave. The previous fix attempt added a lockless i2c_imx->slave = NULL on the error path, but that could race with the ISR under the lock and still cause a NULL pointer dereference.
Fix both issues by deferring the assignment of i2c_imx->slave and i2c_imx->last_slave_event to after a successful resume, and by performing the assignment inside the slave_lock critical section. This guarantees that the slave pointer is never left stale on the error path and is always valid when observed by the interrupt handler.
--- drivers/i2c/busses/i2c-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/i2c/busses/i2c-imx.c +5 -3
@@ -870,9 +870,6 @@ static int i2c_imx_reg_slave(struct i2c_if (i2c_imx->slave)return -EBUSY ; - i2c_imx->slave = client; - i2c_imx->last_slave_event =I2C_SLAVE_STOP ; -/* Resume */ ret =pm_runtime_resume_and_get (i2c_imx->adapter.dev.parent);if (ret <0 ) {
@@ -880,6 +877,11 @@ static int i2c_imx_reg_slave(struct i2c_return ret; } + scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) { + i2c_imx->slave = client; + i2c_imx->last_slave_event =I2C_SLAVE_STOP ; + } +i2c_imx_slave_init (i2c_imx);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 169/261 i2c: imx: Cancel hrtimer before clearing slave pointer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Liem <[email protected]>
commit 6ac7702b6cc2b94aaed9ef2d95bfbefcdc90061f upstream.
In i2c_imx_unreg_slave(), the slave pointer is set to NULL after disabling interrupts. However, a pending interrupt might already have started the hrtimer (i2c_imx_slave_timeout) before the pointer was cleared. If the hrtimer fires after i2c_imx->slave is set to NULL, the timer callback i2c_imx_slave_finish_op() will call i2c_imx_slave_event() with a NULL slave pointer, which results in a use-after-free / NULL pointer dereference.
Fix by canceling the hrtimer and waiting for it to complete after disabling interrupts, before clearing the slave pointer.
--- drivers/i2c/busses/i2c-imx.c | 1 + 1 file changed, 1 insertion(+)
drivers/i2c/busses/i2c-imx.c +1 -0
@@ -900,6 +900,7 @@ static int i2c_imx_unreg_slave(struct i2i2c_imx_reset_regs (i2c_imx); +hrtimer_cancel (&i2c_imx->slave_timer); i2c_imx->slave =NULL ;/* Suspend */
Greg Kroah-Hartman
maintainerPATCH 170/261 can: c_can: c_can_chip_config(): keep controller in init mode until bittiming is configured
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lucas Martins Alves <[email protected]>
commit 26504844613fb44c7cab1c5f6fcff77861709baa upstream.
c_can_chip_config() was programming C_CAN_CTRL_REG without CONTROL_INIT, which may allow the controller to become active before c_can_set_bittiming() finishes.
That creates a short timing window where the peripheral can interact with the bus using a different/default bitrate, potentially generating bus errors and corrupting traffic.
Set CONTROL_INIT together with the control-mode writes in c_can_chip_config() (normal, loopback and listen-only paths), so the controller stays halted until bit timing is fully programmed.
This prevents transient bus disturbance during startup when the configured bitrate differs from the active bus bitrate.
--- drivers/net/can/c_can/c_can_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/net/can/c_can/c_can_main.c +4 -4
@@ -597,20 +597,20 @@ static int c_can_chip_config(struct net_return err;/* enable automatic retransmission */ - priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_ENABLE_AR ); + priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_ENABLE_AR |CONTROL_INIT );if ((priv->can.ctrlmode &CAN_CTRLMODE_LISTENONLY ) && (priv->can.ctrlmode &CAN_CTRLMODE_LOOPBACK )) {/* loopback + silent mode : useful for hot self-test */ - priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST ); + priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST |CONTROL_INIT ); priv->write_reg (priv,C_CAN_TEST_REG ,TEST_LBACK |TEST_SILENT ); }else if (priv->can.ctrlmode &CAN_CTRLMODE_LOOPBACK ) {/* loopback mode : useful for self-test function */ - priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST ); + priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST |CONTROL_INIT ); priv->write_reg (priv,C_CAN_TEST_REG ,TEST_LBACK ); }else if (priv->can.ctrlmode &CAN_CTRLMODE_LISTENONLY ) {/* silent mode : bus-monitoring mode */ - priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST ); + priv->write_reg (priv,C_CAN_CTRL_REG ,CONTROL_TEST |CONTROL_INIT ); priv->write_reg (priv,C_CAN_TEST_REG ,TEST_SILENT ); }
Greg Kroah-Hartman
maintainerPATCH 171/261 can: ems_usb: validate CPC message lengths
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 02925f51377f2a42a6724f00549167499c9302e5 upstream.
ems_usb_read_bulk_callback() walks CPC messages packed in one USB receive buffer.
Check that each declared message fits in the URB payload. Also require the type-specific payload to cover the fields used by the CAN, state, error and overrun handlers.
--- drivers/net/can/usb/ems_usb.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
drivers/net/can/usb/ems_usb.c +43 -0
@@ -409,6 +409,40 @@ static void ems_usb_rx_err(struct ems_usnetif_rx (skb); } +static bool ems_usb_rx_msg_len_valid (struct ems_cpc_msg *msg) +{ +size_t len = msg->length; +size_t can_len; + +switch (msg->type) { +case CPC_MSG_TYPE_CAN_STATE : +return len >=sizeof (msg->msg.can_state); + +case CPC_MSG_TYPE_CAN_FRAME : +case CPC_MSG_TYPE_EXT_CAN_FRAME : +case CPC_MSG_TYPE_RTR_FRAME : +case CPC_MSG_TYPE_EXT_RTR_FRAME : +if (len <CPC_CAN_MSG_MIN_SIZE ) +return false; + +if (msg->type ==CPC_MSG_TYPE_RTR_FRAME || + msg->type ==CPC_MSG_TYPE_EXT_RTR_FRAME ) +return true; + + can_len =can_cc_dlc2len (msg->msg.can_msg.length &0xf ); +return len >=CPC_CAN_MSG_MIN_SIZE + can_len; + +case CPC_MSG_TYPE_CAN_FRAME_ERROR : +return len >=sizeof (msg->msg.error); + +case CPC_MSG_TYPE_OVERRUN : +return len >=sizeof (msg->msg.overrun); + +default : +return true; + } +} +/* * callback for bulk IN urb */
@@ -451,6 +485,15 @@ static void ems_usb_read_bulk_callback(s } msg = (struct ems_cpc_msg *)&ibuf[start]; +if (msg->length > + urb->actual_length - start -CPC_MSG_HEADER_LEN ) { +netdev_err (netdev,"format error\n" ); +break ; + } +if (!ems_usb_rx_msg_len_valid (msg)) { +netdev_err (netdev,"format error\n" ); +break ; + }switch (msg->type) {case CPC_MSG_TYPE_CAN_STATE :
Greg Kroah-Hartman
maintainerPATCH 172/261 can: etas_es58x: es58x_read_bulk_callback(): fix RX buffer leak on URB resubmit failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 7a0cf2b2497c757c3cb1286eddf2986abb0d387b upstream.
es58x_read_bulk_callback() resubmits the RX URB after processing a received packet. If the resubmit succeeds, the URB remains anchored and will be handled by the normal RX path or by teardown.
However, if usb_submit_urb() fails, the callback unanchors the URB and then returns directly. This skips the existing free_urb path, so the coherent transfer buffer allocated with usb_alloc_coherent() is not released.
Reuse the existing free_urb path after a resubmit failure so that the RX coherent buffer is freed before leaving the callback.
--- drivers/net/can/usb/etas_es58x/es58x_core.c | 1 - 1 file changed, 1 deletion(-)
drivers/net/can/usb/etas_es58x/es58x_core.c +0 -1
@@ -1476,7 +1476,6 @@ static void es58x_read_bulk_callback(strdev_err_ratelimited (dev,"Failed resubmitting read bulk urb: %pe\n" ,ERR_PTR (ret)); -return ; free_urb: usb_free_coherent(urb->dev, urb->transfer_buffer_length,
Greg Kroah-Hartman
maintainerPATCH 173/261 can: gs_usb: gs_usb_receive_bulk_callback(): resubmit URB on skb allocation failure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marc Kleine-Budde <[email protected]>
commit 68c5724ecd159992f76edb7b57dc508a44c8b7da upstream.
If the allocation of the SKB in gs_usb_receive_bulk_callback() fails, the driver returns from the callback without resubmitting the URB in order to receive further USB in URBs.
This results in a silent performance degradation which, if it occurs repeatedly, results in starvation of USB in traffic.
Instead of returning immediately, try to resend the URB. If this also fails, this is logged as an info message.
--- drivers/net/can/usb/gs_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/usb/gs_usb.c +2 -2
@@ -671,7 +671,7 @@ static void gs_usb_receive_bulk_callbackif (hf->flags &GS_CAN_FLAG_FD ) { skb =alloc_canfd_skb (netdev, &cfd);if (!skb) -return ; + goto resubmit_urb; cfd->can_id =le32_to_cpu (hf->can_id); cfd->len = data_length;
@@ -684,7 +684,7 @@ static void gs_usb_receive_bulk_callback }else { skb =alloc_can_skb (netdev, &cf);if (!skb) -return ; + goto resubmit_urb; cf->can_id =le32_to_cpu (hf->can_id);can_frame_set_cc_len (cf, hf->can_dlc, dev->can.ctrlmode);
Greg Kroah-Hartman
maintainerPATCH 174/261 can: j1939: transport: j1939_session_fresh_new(): initialize receive buffer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oleksij Rempel <[email protected]>
commit eb96c58907922546e415e545fe9a14ea63b02719 upstream.
Zero the allocated buffer in j1939_session_fresh_new() to ensure it contains no residual data.
While there is a potential performance impact if users allocate maximum sized ETP buffers, most real-world use cases are not noticeably affected since the maximum known buffer size is typically around 65K.
Message-ID: <CAPAUci5dykCLjoijqkUtFqJFesgncrD7+S6y_V=gjbFkY2Tifg@mail.gmail.com>
--- net/can/j1939/transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/can/j1939/transport.c +1 -1
@@ -1562,7 +1562,7 @@ j1939_session *j1939_session_fresh_new(s }/* alloc data area */ -skb_put (skb, size); +skb_put_zero (skb, size);/* skb is recounted in j1939_session_new() */ return session; }
Greg Kroah-Hartman
maintainerPATCH 175/261 can: j1939: use netdevice_tracker for j1939_{priv,session,ecu} tracking
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tetsuo Handa <[email protected]>
commit d2fb981384b3a45f690616d550b29046e8ad16a4 upstream.
syzbot is still reporting
unregister_netdevice: waiting for vcan0 to become free. Usage count = 2
problem. A debug printk() patch in linux-next-20260508 identified that there is dev_hold()/dev_put() imbalance in j1939_priv management.
Call trace for vcan0[26] +4 at
__dev_hold include/linux/netdevice.h:4470 [inline]
netdev_hold include/linux/netdevice.h:4513 [inline]
dev_hold include/linux/netdevice.h:4536 [inline]
j1939_priv_create net/can/j1939/main.c:140 [inline]
j1939_netdev_start+0x36b/0xc10 net/can/j1939/main.c:268
j1939_sk_bind+0x853/0xb30 net/can/j1939/socket.c:506
__sys_bind_socket net/socket.c:1948 [inline]
__sys_bind+0x2e9/0x410 net/socket.c:1979Call trace for vcan0[28] -3 at
__dev_put include/linux/netdevice.h:4456 [inline]
netdev_put include/linux/netdevice.h:4523 [inline]
dev_put include/linux/netdevice.h:4548 [inline]
__j1939_priv_release net/can/j1939/main.c:166 [inline]
kref_put include/linux/kref.h:65 [inline]
j1939_priv_put+0x128/0x270 net/can/j1939/main.c:172
j1939_sk_sock_destruct+0x52/0x90 net/can/j1939/socket.c:388
__sk_destruct+0x8d/0x9d0 net/core/sock.c:2352
rcu_do_batch kernel/rcu/tree.c:2617 [inline]
rcu_core kernel/rcu/tree.c:2869 [inline]
rcu_cpu_kthread+0x99e/0x1470 kernel/rcu/tree.c:2957
smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245This refcount leak in j1939_priv might be caused by a refcount leak in j1939_{session,ecu} because j1939_{session,ecu} holds a ref on j1939_priv. For further investigation using upstream kernels, enable netdevice_tracker in j1939_{priv,session,ecu} management.
--- net/can/j1939/bus.c | 2 ++ net/can/j1939/j1939-priv.h | 3 +++ net/can/j1939/main.c | 8 ++++---- net/can/j1939/transport.c | 2 ++ 4 files changed, 11 insertions(+), 4 deletions(-)
net/can/j1939/bus.c +2 -0
@@ -20,6 +20,7 @@ static void __j1939_ecu_release(struct kstruct j1939_priv *priv = ecu->priv;list_del (&ecu->list); +netdev_put (priv->ndev, &ecu->priv_dev_tracker);kfree (ecu);j1939_priv_put (priv); }
@@ -155,6 +156,7 @@ struct j1939_ecu *j1939_ecu_create_lockeif (!ecu)return ERR_PTR (-ENOMEM );kref_init (&ecu->kref); +netdev_hold (priv->ndev, &ecu->priv_dev_tracker,gfp_any ()); ecu->addr =J1939_IDLE_ADDR ; ecu->name = name;
net/can/j1939/j1939-priv.h +3 -0
@@ -38,6 +38,7 @@ struct j1939_ecu {struct hrtimer ac_timer;struct kref kref;struct j1939_priv *priv; +netdevice_tracker priv_dev_tracker;/* count users, to help transport protocol decide for interaction */ int nusers;
@@ -60,6 +61,7 @@ struct j1939_priv {rwlock_t lock;struct net_device *ndev; +netdevice_tracker dev_tracker;/* list of 256 ecu ptrs, that cache the claimed addresses. * also protected by the above lock
@@ -229,6 +231,7 @@ enum j1939_session_state {struct j1939_session {struct j1939_priv *priv; +netdevice_tracker priv_dev_tracker;struct list_head active_session_list_entry;struct list_head sk_session_queue_entry;struct kref kref;
net/can/j1939/main.c +4 -4
@@ -137,7 +137,7 @@ static struct j1939_priv *j1939_priv_cre priv->ndev = ndev;kref_init (&priv->kref);kref_init (&priv->rx_kref); -dev_hold (ndev); +netdev_hold (ndev, &priv->dev_tracker,GFP_KERNEL );netdev_dbg (priv->ndev,"%s : 0x%p\n" , __func__, priv);
@@ -163,7 +163,7 @@ static void __j1939_priv_release(structWARN_ON_ONCE (!list_empty (&priv->ecus));WARN_ON_ONCE (!list_empty (&priv->j1939_socks)); -dev_put (ndev); +netdev_put (ndev, &priv->dev_tracker);kfree (priv); }
@@ -281,7 +281,7 @@ struct j1939_priv *j1939_netdev_start(st*/ kref_get (&priv_new->rx_kref);mutex_unlock (&j1939_netdev_lock); -dev_put (ndev); +netdev_put (ndev, &priv->dev_tracker);kfree (priv);return priv_new; }
@@ -298,7 +298,7 @@ struct j1939_priv *j1939_netdev_start(stj1939_priv_set (ndev,NULL );mutex_unlock (&j1939_netdev_lock); -dev_put (ndev); +netdev_put (ndev, &priv->dev_tracker);kfree (priv);return ERR_PTR (ret);
net/can/j1939/transport.c +2 -0
@@ -282,6 +282,7 @@ static void j1939_session_destroy(structkfree_skb (skb); }__j1939_session_drop (session); +netdev_put (session->priv->ndev, &session->priv_dev_tracker);j1939_priv_put (session->priv);kfree (session); }
@@ -1510,6 +1511,7 @@ static struct j1939_session *j1939_sessiINIT_LIST_HEAD (&session->active_session_list_entry);INIT_LIST_HEAD (&session->sk_session_queue_entry);kref_init (&session->kref); +netdev_hold (priv->ndev, &session->priv_dev_tracker,gfp_any ());j1939_priv_get (priv); session->priv = priv;
Greg Kroah-Hartman
maintainerPATCH 176/261 can: kvaser_usb: kvaser_usb_hydra_get_busparams(): fix memory leak in kvaser_usb_hydra_get_busparams()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abdun Nihaal <[email protected]>
commit 941eaf9a6d3b33dea49f2c0a1da7546a03b6ff71 upstream.
The memory allocated for cmd is not freed after the call to kvaser_usb_send_cmd() in both the normal and error paths. Fix that by adding a kfree() immediately after the call.
--- drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +1 -0
@@ -1610,6 +1610,7 @@ static int kvaser_usb_hydra_get_busparamreinit_completion (&priv->get_busparams_comp); err =kvaser_usb_send_cmd (dev, cmd, cmd_len); +kfree (cmd);if (err)return err;
Greg Kroah-Hartman
maintainerPATCH 177/261 can: kvaser_usb_leaf: kvaser_usb_leaf_wait_cmd(): validate received command extents
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 0293dd153f9dbc1ddf5dacdccc76b363bce4a8ee upstream.
The wait and bulk receive paths walk variable-length commands from a USB buffer. A nonzero command shorter than CMD_HEADER_LEN can still be dispatched, and the wait path copies a matching command into a fixed caller-owned struct kvaser_cmd using the device-provided length.
Reject nonzero commands that do not contain the fixed header or that extend beyond the current USB buffer item. In the wait path, also reject a matching command that exceeds the destination before copying it.
--- drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c +11 -2
@@ -617,13 +617,22 @@ static int kvaser_usb_leaf_wait_cmd(conscontinue ; } -if (pos + tmp->len > actual_len) { +if (tmp->len <CMD_HEADER_LEN || + tmp->len > actual_len - pos) {dev_err_ratelimited (&dev->intf->dev,"Format error\n" );break ; }if (tmp->id == id) { +if (tmp->len >sizeof (*cmd)) { +dev_err_ratelimited (&dev->intf->dev, +"Received command %u too large (%u)\n" , + tmp->id, tmp->len); + err = -EIO ; + goto end; + } +memcpy (cmd, tmp, tmp->len); goto end; }
@@ -1595,7 +1604,7 @@ static void kvaser_usb_leaf_read_bulk_cacontinue ; } -if (pos + cmd->len > len) { +if (cmd->len <CMD_HEADER_LEN || cmd->len > len - pos) {dev_err_ratelimited (&dev->intf->dev,"Format error\n" );break ; }
Greg Kroah-Hartman
maintainerPATCH 178/261 can: softing: fw_parse(): validate firmware record spans
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 856d6cb04e5407523566b075841dcd6423757d1c upstream.
fw_parse() reads a fixed record header, a firmware-provided payload, and a trailing checksum without knowing the end of the firmware blob. A truncated record can therefore make those reads exceed the blob.
The same record also supplies addresses and lengths for writes into DPRAM. The generic loader uses wrap-prone mixed signed arithmetic for its bounds check, while the application loader does not bound the staging copy at all.
Pass the firmware end to the parser and validate the full source record. Use a signed wide offset for generic DPRAM records and validate the application staging span against the mapped DPRAM before copying.
--- drivers/net/can/softing/softing_fw.c | 46 ++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 14 deletions(-)
drivers/net/can/softing/softing_fw.c +32 -14
@@ -91,12 +91,12 @@ int softing_bootloader_command(struct soreturn ret; } -static int fw_parse (const uint8_t **pmem,uint16_t *ptype,uint32_t *paddr, -uint16_t *plen,const uint8_t **pdat) +static int fw_parse (const u8 **pmem,const u8 *limit,u16 *ptype, +u32 *paddr,u16 *plen,const u8 **pdat) {uint16_t checksum[2 ]; -const uint8_t *mem; -const uint8_t *end; +const u8 *mem; +const u8 *record_end;/* * firmware records are a binary, unaligned stream composed of:
@@ -114,14 +114,21 @@ static int fw_parse(const uint8_t **pmem* endianness & alignment. */ mem = *pmem; +/* A record needs an 8-byte prefix and a 2-byte checksum. */ +if (mem > limit || limit - mem <10 ) +return -EINVAL ; + *ptype =le16_to_cpup ((void *)&mem[0 ]); *paddr =le32_to_cpup ((void *)&mem[2 ]); *plen =le16_to_cpup ((void *)&mem[6 ]); +if (*plen > limit - mem -10 ) +return -EINVAL ; + *pdat = &mem[8 ];/* verify checksum */ - end = &mem[8 + *plen]; - checksum[0 ] =le16_to_cpup ((void *)end); -for (checksum[1 ] =0 ; mem < end; ++mem) + record_end = &mem[8 + *plen]; + checksum[0 ] =le16_to_cpup ((void *)record_end); +for (checksum[1 ] =0 ; mem < record_end; ++mem) checksum[1 ] += *mem;if (checksum[0 ] != checksum[1 ])return -EINVAL ;
@@ -139,6 +146,7 @@ int softing_load_fw(const char *file, stuint16_t type, len;uint32_t addr;uint8_t *buf =NULL , *new_buf; +s64 dpram_offset;int buflen =0 ;int8_t type_end =0 ;
@@ -153,7 +161,7 @@ int softing_load_fw(const char *file, st mem = fw->data; end = &mem[fw->size];/* look for header record */ - ret =fw_parse (&mem, &type, &addr, &len, &dat); + ret =fw_parse (&mem, end, &type, &addr, &len, &dat);if (ret <0 ) goto failed;if (type !=0xffff )
@@ -164,7 +172,7 @@ int softing_load_fw(const char *file, st }/* ok, we had a header */ while (mem < end) { - ret =fw_parse (&mem, &type, &addr, &len, &dat); + ret =fw_parse (&mem, end, &type, &addr, &len, &dat);if (ret <0 ) goto failed;if (type ==3 ) {
@@ -179,9 +187,13 @@ int softing_load_fw(const char *file, st goto failed; } -if ((addr + len + offset) > size) + dpram_offset = (s64 )addr + offset; +if (dpram_offset <0 || dpram_offset > size || + len > size - dpram_offset) { + ret = -EINVAL ; goto failed; -memcpy_toio (&dpram[addr + offset], dat, len); + } +memcpy_toio (&dpram[dpram_offset], dat, len);/* be sure to flush caches from IO space */ mb ();if (len > buflen) {
@@ -195,7 +207,7 @@ int softing_load_fw(const char *file, st buf = new_buf; }/* verify record data */ -memcpy_fromio (buf, &dpram[addr + offset], len); +memcpy_fromio (buf, &dpram[dpram_offset], len);if (memcmp (buf, dat, len)) {/* is not ok */ dev_alert (&card->pdev->dev,"DPRAM readback failed\n" );
@@ -237,7 +249,7 @@ int softing_load_app_fw(const char *file mem = fw->data; end = &mem[fw->size];/* look for header record */ - ret =fw_parse (&mem, &type, &addr, &len, &dat); + ret =fw_parse (&mem, end, &type, &addr, &len, &dat);if (ret) goto failed; ret = -EINVAL ;
@@ -253,7 +265,7 @@ int softing_load_app_fw(const char *file }/* ok, we had a header */ while (mem < end) { - ret =fw_parse (&mem, &type, &addr, &len, &dat); + ret =fw_parse (&mem, end, &type, &addr, &len, &dat);if (ret) goto failed;
@@ -279,6 +291,12 @@ int softing_load_app_fw(const char *file/* work in 16bit (target) */ sum &=0xffff ; +if (card->pdat->app.offs > card->dpram_size || + len > card->dpram_size - card->pdat->app.offs) { + ret = -EINVAL ; + goto failed; + } +memcpy_toio (&card->dpram[card->pdat->app.offs], dat, len);iowrite32 (card->pdat->app.offs + card->pdat->app.addr, &card->dpram[DPRAM_COMMAND +2 ]);
Greg Kroah-Hartman
maintainerPATCH 179/261 can: peak_usb: add bounds check for USB channel index
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: James Gao <[email protected]>
commit 39132f166ca8ce00ae60d8a9068e06a60943cc4b upstream.
The channel control index ctrl_idx is derived from rx->len which comes directly from a device USB payload. The mask 0x0f allows values 0-15, but the array size of usb_if->dev[] is only 2. Values 2-15 cause heap out-of-bounds read, eventually causing kernel panic in the IRQ context.
Add bounds checking for ctrl_idx before the array access in both pcan_usb_pro_handle_canmsg() and pcan_usb_pro_handle_error().
--- drivers/net/can/usb/peak_usb/pcan_usb_pro.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-)
drivers/net/can/usb/peak_usb/pcan_usb_pro.c +16 -4
@@ -534,12 +534,18 @@ static int pcan_usb_pro_handle_canmsg(st struct pcan_usb_pro_rxmsg *rx) {const unsigned int ctrl_idx = (rx->len >>4 ) &0x0f ; -struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; -struct net_device *netdev = dev->netdev; +struct peak_usb_device *dev; +struct net_device *netdev;struct can_frame *can_frame;struct sk_buff *skb;struct skb_shared_hwtstamps *hwts; +if (ctrl_idx >=ARRAY_SIZE (usb_if->dev)) +return -EINVAL ; + + dev = usb_if->dev[ctrl_idx]; + netdev = dev->netdev; + skb =alloc_can_skb (netdev, &can_frame);if (!skb)return -ENOMEM ;
@@ -573,14 +579,20 @@ static int pcan_usb_pro_handle_error(str {const u16 raw_status =le16_to_cpu (er->status);const unsigned int ctrl_idx = (er->channel >>4 ) &0x0f ; -struct peak_usb_device *dev = usb_if->dev[ctrl_idx]; -struct net_device *netdev = dev->netdev; +struct peak_usb_device *dev; +struct net_device *netdev;struct can_frame *can_frame;enum can_state new_state =CAN_STATE_ERROR_ACTIVE ;u8 err_mask =0 ;struct sk_buff *skb;struct skb_shared_hwtstamps *hwts; +if (ctrl_idx >=ARRAY_SIZE (usb_if->dev)) +return -EINVAL ; + + dev = usb_if->dev[ctrl_idx]; + netdev = dev->netdev; +/* nothing should be sent while in BUS_OFF state */ if (dev->can.state ==CAN_STATE_BUS_OFF )return 0 ;
Greg Kroah-Hartman
maintainerPATCH 180/261 can: peak_usb: peak_usb_start(): fix double free of transfer buffer on URB submit error
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maoyi Xie <[email protected]>
commit 9b3d5a6d952c38bbcf07f903cbeadefdb56b9bc9 upstream.
In peak_usb_start(), each RX URB transfer buffer is allocated with kmalloc() and the URB is flagged URB_FREE_BUFFER so that the final usb_free_urb() also frees the transfer buffer.
If usb_submit_urb() fails, the error path frees the buffer explicitly with kfree(buf) and then calls usb_free_urb(urb). Because URB_FREE_BUFFER is set, usb_free_urb() -> urb_destroy() frees the same buffer a second time, a double free of the transfer buffer.
BUG: KASAN: double-free in usb_free_urb.part.0+0x91/0xb0 Free of addr ffff8881069ccb80 by task trigger.sh/285 Call Trace: kfree+0x113/0x3c0 usb_free_urb.part.0+0x91/0xb0
Drop the redundant kfree(buf); usb_free_urb() already releases the transfer buffer. This mirrors commit 03819abbeb11 ("net: usb: lan78xx: Fix double free issue with interrupt buffer allocation").
--- drivers/net/can/usb/peak_usb/pcan_usb_core.c | 1 - 1 file changed, 1 deletion(-)
drivers/net/can/usb/peak_usb/pcan_usb_core.c +0 -1
@@ -470,7 +470,6 @@ static int peak_usb_start(struct peak_usnetif_device_detach (dev->netdev);usb_unanchor_urb (urb); -kfree (buf);usb_free_urb (urb);break ; }
Greg Kroah-Hartman
maintainerPATCH 181/261 can: peak_usb: validate uCAN receive record lengths
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 93fcab2c6968446316bbb49548848df604d6346f upstream.
pcan_usb_fd_decode_buf() walks uCAN records packed in one USB receive buffer.
Require each record to contain the fixed header for its type, and verify CAN payload bytes before copying them into the skb.
--- drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 40 ++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-)
drivers/net/can/usb/peak_usb/pcan_usb_fd.c +39 -1
@@ -565,6 +565,13 @@ static int pcan_usb_fd_decode_canmsg(str dev->can.ctrlmode); } +if (!(rx_msg_flags &PUCAN_MSG_RTR ) && +le16_to_cpu (rx_msg->size) - offsetof(struct pucan_rx_msg , d) < + cfd->len) { +kfree_skb (skb); +return -EBADMSG ; + } + cfd->can_id =le32_to_cpu (rm->can_id);if (rx_msg_flags &PUCAN_MSG_EXT_ID )
@@ -713,6 +720,24 @@ static void pcan_usb_fd_decode_ts(structpeak_usb_set_ts_now (&usb_if->time_ref,le32_to_cpu (ts->ts_low)); } +static size_t pcan_usb_fd_rx_msg_min_size (u16 rx_msg_type) +{ +switch (rx_msg_type) { +case PUCAN_MSG_CAN_RX : +return offsetof(struct pucan_rx_msg , d); +case PCAN_UFD_MSG_CALIBRATION : +return sizeof (struct pcan_ufd_ts_msg ); +case PUCAN_MSG_ERROR : +return sizeof (struct pucan_error_msg ); +case PUCAN_MSG_STATUS : +return sizeof (struct pucan_status_msg ); +case PCAN_UFD_MSG_OVERRUN : +return sizeof (struct pcan_ufd_ovr_msg ); +default : +return sizeof (struct pucan_msg ); + } +} +/* callback for bulk IN urb */ static int pcan_usb_fd_decode_buf (struct peak_usb_device *dev,struct urb *urb) {
@@ -727,6 +752,12 @@ static int pcan_usb_fd_decode_buf(struct msg_end = urb->transfer_buffer + urb->actual_length;for (; msg_ptr < msg_end;) {u16 rx_msg_type, rx_msg_size; +size_t rx_msg_min_size; + +if (msg_end - msg_ptr <sizeof (*rx_msg)) { + err = -EBADMSG ; +break ; + } rx_msg = (struct pucan_msg *)msg_ptr;if (!rx_msg->size) {
@@ -738,12 +769,19 @@ static int pcan_usb_fd_decode_buf(struct rx_msg_type =le16_to_cpu (rx_msg->type);/* check if the record goes out of current packet */ -if (msg_ptr + rx_msg_size > msg_end) { +if (rx_msg_size > msg_end - msg_ptr) {netdev_err (netdev,"got frag rec: should inc usb rx buf sze\n" ); err = -EBADMSG ;break ; } + + rx_msg_min_size =pcan_usb_fd_rx_msg_min_size (rx_msg_type); +if (rx_msg_size < rx_msg_min_size) { +netdev_err (netdev,"got short rec\n" ); + err = -EBADMSG ; +break ; + }switch (rx_msg_type) {case PUCAN_MSG_CAN_RX :
Greg Kroah-Hartman
maintainerPATCH 182/261 can: ctucanfd: add missing MODULE_DEVICE_TABLE()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit d937bdb244a751fe5967052ea2d64a7b2c476cc0 upstream.
The driver has a match table for the pci bus wired into its driver structure, but the table is not exported with MODULE_DEVICE_TABLE().
Add the missing MODULE_DEVICE_TABLE() entry so module alias information is generated for automatic module loading.
This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the driver registration structure, and the missing module alias publication.
--- drivers/net/can/ctucanfd/ctucanfd_pci.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/can/ctucanfd/ctucanfd_pci.c +1 -0
@@ -274,6 +274,7 @@ static const struct pci_device_id ctucan CTUCAN_WITH_CTUCAN_ID)}, {}, }; +MODULE_DEVICE_TABLE (pci, ctucan_pci_tbl);static struct pci_driver ctucan_pci_driver = { .name =KBUILD_MODNAME ,
Greg Kroah-Hartman
maintainerPATCH 183/261 can: ctucanfd: use self-test mode for PRESUME_ACK
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
commit c31a435933f18be0f874302161333e9f16e200a0 upstream.
Use self-test mode for CAN_CTRLMODE_PRESUME_ACK so transmitted frames can complete without receiving an ACK.
ACK forbidden mode prevents the controller from acknowledging received frames and does not implement the presume-ack behavior.
--- drivers/net/can/ctucanfd/ctucanfd_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +2 -2
@@ -340,8 +340,8 @@ static void ctucan_set_mode(struct ctuca (mode_reg & ~REG_MODE_FDE ); mode_reg = (mode->flags &CAN_CTRLMODE_PRESUME_ACK ) ? - (mode_reg |REG_MODE_ACF ) : - (mode_reg & ~REG_MODE_ACF ); + (mode_reg |REG_MODE_STM ) : + (mode_reg & ~REG_MODE_STM ); mode_reg = (mode->flags &CAN_CTRLMODE_FD_NON_ISO ) ? (mode_reg | REG_MODE_NISOFD) :
Greg Kroah-Hartman
maintainerPATCH 184/261 can: ctucanfd: unmap BAR0 using base address
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
commit a6873910f983096746d1a2e0af94f36b8003e839 upstream.
BAR0 is mapped into bar0_base, while cra_addr points to an offset within that mapping and is used for other purposes.
Pass bar0_base to pci_iounmap(), instead of cra_addr, on the probe error path so the address returned by pci_iomap() is used for unmapping.
--- drivers/net/can/ctucanfd/ctucanfd_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/can/ctucanfd/ctucanfd_pci.c +1 -1
@@ -202,7 +202,7 @@ err_free_board:pci_set_drvdata (pdev,NULL );kfree (bdata); err_pci_iounmap_bar0: -pci_iounmap (pdev, cra_addr); +pci_iounmap (pdev, bar0_base); err_pci_iounmap_bar1:pci_iounmap (pdev, addr); err_release_regions:
Greg Kroah-Hartman
maintainerPATCH 185/261 can: ctucanfd: handle bus error interrupts
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
commit e74bae899529f49c0f375307983d12e8ecad7d4b upstream.
Include REG_INT_STAT_BEI in the top-level error interrupt condition.
BEI is enabled when CAN_CTRLMODE_BERR_REPORTING is requested and ctucan_err_interrupt() already handles it. Without checking and clearing BEI in the top-level handler, bus error interrupts are not handled or acknowledged.
--- drivers/net/can/ctucanfd/ctucanfd_base.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +6 -2
@@ -1135,8 +1135,12 @@ static irqreturn_t ctucan_interrupt(int/* Error interrupts */ if (FIELD_GET (REG_INT_STAT_EWLI , isr) ||FIELD_GET (REG_INT_STAT_FCSI , isr) || -FIELD_GET (REG_INT_STAT_ALI , isr)) { - icr = isr & (REG_INT_STAT_EWLI |REG_INT_STAT_FCSI |REG_INT_STAT_ALI ); +FIELD_GET (REG_INT_STAT_ALI , isr) || +FIELD_GET (REG_INT_STAT_BEI , isr)) { + icr = isr & (REG_INT_STAT_EWLI | +REG_INT_STAT_FCSI | +REG_INT_STAT_ALI | +REG_INT_STAT_BEI );ctucan_netdev_dbg (ndev,"some ERR interrupt: clearing 0x%08x\n" , icr);ctucan_write32 (priv,CTUCANFD_INT_STAT , icr);
Greg Kroah-Hartman
maintainerPATCH 186/261 can: ctucanfd: mark error-active controller status valid
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avi Weiss <[email protected]>
commit 4e735cbe3affe88001428fdd9cae8e685ce92f21 upstream.
In the CAN_STATE_ERROR_ACTIVE case, cf->data[1] is set to CAN_ERR_CRTL_ACTIVE, but cf->can_id is not set with CAN_ERR_CRTL in that path.
Set CAN_ERR_CRTL so consumers know the controller-status information in cf->data[1] is valid.
--- drivers/net/can/ctucanfd/ctucanfd_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/can/ctucanfd/ctucanfd_base.c +1 -1
@@ -868,7 +868,7 @@ static void ctucan_err_interrupt(structbreak ;case CAN_STATE_ERROR_ACTIVE :if (skb) { - cf->can_id |=CAN_ERR_CNT ; + cf->can_id |=CAN_ERR_CRTL |CAN_ERR_CNT ; cf->data[1 ] =CAN_ERR_CRTL_ACTIVE ; cf->data[6 ] = bec.txerr; cf->data[7 ] = bec.rxerr;
Greg Kroah-Hartman
maintainerPATCH 187/261 drm/dp: Read the PCON max FRL bandwidth only for HDMI DFPs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Kaplan <[email protected]>
commit e40e20ac089e32f1d910636155dc82e61e61dcf3 upstream.
The PCON max FRL bandwidth field lives in byte 2 of the DFP Detailed Capability Info (DPCD 0x82 for the first DFP).
The DP standard defines the meaning of descriptor bytes 1-3 strictly per DFP type, and for a DisplayPort type DFP all of them are reserved, with "read all 0s" semantics (DP v2.0, section 2.12.3, Table 2-183).
The FRL bandwidth field is an HDMI DFP extension added by the VESA DP-to-HDMI PCON specification. drm_dp_get_pcon_max_frl_bw() however parses the byte without checking the DFP type, the branch presence or DETAILED_CAP_INFO_AVAILABLE. Without the latter the port descriptors are one byte wide and port_cap[2] is not even the right register.
All neighbouring helpers parsing the same descriptor are scoped by the DFP type already, see for instance drm_dp_downstream_max_bpc() reading the same byte and returning 0 for a DP type DFP. amdgpu's DC parses the field only for HDMI(/DP++) detailed types as well.
This is not theoretical.
A Synaptics VMM7100 based USB-C to HDMI adapter with a macOS targeted firmware advertises a DisplayPort type DFP with the type byte replicated across the whole descriptor (08 08 08 08).
i915 decodes that as "PCON limited to 18 Gbps FRL" and prunes every mode above ~750 MHz dotclock, including all the 4k@100/120 modes the sink EDID offers, while macOS drives 4k@120 through the same adapter just fine via DP DSC (and amdgpu's type-scoped parser would ignore the bogus field as well).
Only parse the field for an HDMI DFP behind a DPCD 1.1+ branch device that reports detailed cap info, matching the type-scoped field layout of the spec and the rest of the helpers.
--- drivers/gpu/drm/display/drm_dp_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/gpu/drm/display/drm_dp_helper.c +12 -0
@@ -2930,6 +2930,18 @@ int drm_dp_get_pcon_max_frl_bw(const u8int bw;u8 buf; +if (!drm_dp_is_branch (dpcd)) +return 0 ; + +if (dpcd[DP_DPCD_REV ] <0x11 ) +return 0 ; + +if ((dpcd[DP_DOWNSTREAMPORT_PRESENT ] &DP_DETAILED_CAP_INFO_AVAILABLE ) ==0 ) +return 0 ; + +if ((port_cap[0 ] &DP_DS_PORT_TYPE_MASK ) !=DP_DS_PORT_TYPE_HDMI ) +return 0 ; + buf = port_cap[2 ]; bw = buf &DP_PCON_MAX_FRL_BW ;
Greg Kroah-Hartman
maintainerPATCH 188/261 drm/vc4: Supply the overflow slot size in BPOS, not the whole bin BO size
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jose Maria Casanova Crespo <[email protected]>
commit 6395789e4739aa5177bbec0fa0f07ccc38d249b0 upstream.
vc4_overflow_mem_work() points BPOA at a 512KB slot inside the 16MB binner BO, but writes the size of the whole BO to BPOS. On every binner out-of-memory event the PTB is therefore authorized to write tile lists across all the other slots (which may hold the tile state, tile alloc and overflow memory of in-flight jobs) and, for any slot but the first, past the end of the binner BO into unrelated CMA memory.
Since CMA pages are recycled into page cache and user allocations, this is arbitrary memory corruption by GPU DMA. In practice it shows up as GPU hangs with corrupted control list pointers, userspace heap corruption, a GPU that stays permanently wedged after the first hang, and occasional full system crashes, whenever a job overflows the initial binner slot.
The bug dates back to the conversion from a dedicated overflow BO (where writing the full BO size was correct) to the slotted binner BO.
--- drivers/gpu/drm/vc4/vc4_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/vc4/vc4_irq.c +1 -1
@@ -104,7 +104,7 @@ vc4_overflow_mem_work(struct work_struct vc4->bin_alloc_overflow =BIT (bin_bo_slot);V3D_WRITE (V3D_BPOA , bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size); -V3D_WRITE (V3D_BPOS , bo->base.base.size); +V3D_WRITE (V3D_BPOS , vc4->bin_alloc_size);V3D_WRITE (V3D_INTCTL ,V3D_INT_OUTOMEM );V3D_WRITE (V3D_INTENA ,V3D_INT_OUTOMEM );spin_unlock_irqrestore (&vc4->job_lock, irqflags);
Greg Kroah-Hartman
maintainerPATCH 189/261 drm/vc4: Zero the tile state data array before each BIN job
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maíra Canal <[email protected]>
commit 48a570c964d8e37d353381e4195106277e17f5cb upstream.
The binner BO is a single 16MB buffer split into 512KB slots that are handed out to jobs at submission time and recycled as jobs complete, without ever being cleared. Each slot holds the job's Tile State Data Array (TSDA) at its start, followed by the tile allocation pool.
While the tile allocation pool is only walked by the render thread through branches the binner generated during the current job, the TSDA is the PTB's own per-tile bookkeeping and is consumed by the hardware itself. Although the kernel sets the "Auto-initialise Tile State Data Array" flag in the tile binning mode configuration, the PTB demonstrably still acts on stale tile state left by the slot's previous user: the binner ends up creating invalid command streams with invalid primitive streams and branches, which can cause GPU hangs as observed in [1][2].
Zero the TSDA when the job's binning slot is configured. This clears 48 bytes per tile (~24KB for a 1080p frame) in the submission path, and guarantees the PTB never sees another job's tile state.
The tile count is only checked for being non-zero today, so the 8-bit fields it comes from can describe a tile state array almost six times larger than the slot it has to live in. Bound it before the slot is handed out, since such size decides how much of the slot is left for the tile alloc pool.
--- drivers/gpu/drm/vc4/vc4_validate.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-)
drivers/gpu/drm/vc4/vc4_validate.c +23 -6
@@ -387,6 +387,23 @@ validate_tile_binning_config(VALIDATE_ARreturn -EINVAL ; } +/* The tile state data array is 48 bytes per tile, and we put it at +* the start of a BO containing both it and the tile alloc. +*/ + tile_state_size =48 * tile_count; + +/* Since the tile alloc array will follow us, align. */ + tile_state_size =roundup (tile_state_size,4096 ); + +/* Reject configurations whose tile state would leave no room for +* the tile alloc pool that follows it in the slot. +*/ +if (tile_state_size >= vc4->bin_alloc_size) { +DRM_DEBUG ("Tile binning config of %dx%d too large\n" , + exec->bin_tiles_x, exec->bin_tiles_y); +return -EINVAL ; + } + bin_slot =vc4_v3d_get_bin_slot (vc4);if (bin_slot <0 ) {if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS ) {
@@ -402,13 +419,13 @@ validate_tile_binning_config(VALIDATE_AR exec->bin_slots |=BIT (bin_slot); bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size; -/* The tile state data array is 48 bytes per tile, and we put it at -* the start of a BO containing both it and the tile alloc. -*/ - tile_state_size =48 * tile_count; + exec->tile_alloc_offset = bin_addr + tile_state_size; -/* Since the tile alloc array will follow us, align. */ - exec->tile_alloc_offset = bin_addr +roundup (tile_state_size,4096 ); +/* The TSDA area must be zeroed out before use, otherwise the PTB might +* consume a stale tile state. +*/ +memset (vc4->bin_bo->base.vaddr + bin_slot * vc4->bin_alloc_size,0 , + tile_state_size); *(uint8_t *)(validated + 14) = ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
Greg Kroah-Hartman
maintainerPATCH 190/261 drm/mediatek: ovl_adaptor: balance component registrations
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 533e3469a57996905cdb95f178e7efe38c21aeb2 upstream.
The OVL adaptor registers both an aggregate driver for its child devices and a component for the main DRM aggregate. Probe currently ignores an error from registering the child aggregate and leaves that aggregate registered if registering the DRM component fails. The remove callback also leaves the DRM component registered.
These imbalances can leave component framework entries referring to a device whose probe failed or whose driver has been detached. The aggregate unbind callback also fails to undo component_bind_all(), leaving its child components marked as bound when the aggregate is removed.
Check the aggregate registration result, unwind it when the component registration fails, and unregister the component before the aggregate on remove. Keep runtime PM enabled until both framework registrations have been removed, and unbind all child components from the aggregate unbind callback.
--- drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +6 -1
@@ -494,6 +494,7 @@ static void mtk_disp_ovl_adaptor_master_struct mtk_disp_ovl_adaptor *priv =dev_get_drvdata (dev); priv->children_bound = false; +component_unbind_all (dev, priv->mmsys_dev); }static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = {
@@ -520,12 +521,15 @@ static int mtk_disp_ovl_adaptor_probe(st priv->mmsys_dev = pdev->dev.platform_data; -component_master_add_with_match (dev, &mtk_disp_ovl_adaptor_master_ops, match); + ret =component_master_add_with_match (dev, &mtk_disp_ovl_adaptor_master_ops, match); +if (ret) +return dev_err_probe (dev, ret,"Failed to add component master\n" );pm_runtime_enable (dev); ret =component_add (dev, &mtk_disp_ovl_adaptor_comp_ops);if (ret !=0 ) { +component_master_del (dev, &mtk_disp_ovl_adaptor_master_ops);pm_runtime_disable (dev);dev_err (dev,"Failed to add component: %d\n" , ret); }
@@ -535,6 +539,7 @@ static int mtk_disp_ovl_adaptor_probe(ststatic int mtk_disp_ovl_adaptor_remove (struct platform_device *pdev) { +component_del (&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops);component_master_del (&pdev->dev, &mtk_disp_ovl_adaptor_master_ops);pm_runtime_disable (&pdev->dev);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 191/261 drm/amdgpu: restore UMD profile pstate after runtime resume
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Candice Li <[email protected]>
commit f931c54b241ce2f36bfc34955aec43a188276b8d upstream.
Runtime suspend runs GFX hw_fini and clears perfmon clock gating while the UMD profile DPM level remains set in software. Re-apply stable pstate after a successful runtime resume when a profile mode is active.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +15 -0
@@ -2637,6 +2637,19 @@ static int amdgpu_pmops_runtime_suspend(return 0 ; } +static void amdgpu_restore_umd_profile_pstate_after_runpm (struct amdgpu_device *adev) +{ +enum amd_dpm_forced_level level; +uint32_t profile_mode_mask =AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD | +AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK | +AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK | +AMD_DPM_FORCED_LEVEL_PROFILE_PEAK ; + + level =amdgpu_dpm_get_performance_level (adev); +if (level & profile_mode_mask) +amdgpu_asic_update_umd_stable_pstate (adev, true); +} +static int amdgpu_pmops_runtime_resume (struct device *dev) {struct pci_dev *pdev =to_pci_dev (dev);
@@ -2680,6 +2693,8 @@ static int amdgpu_pmops_runtime_resume(sif (amdgpu_device_supports_px (drm_dev)) drm_dev->switch_power_state =DRM_SWITCH_POWER_ON ; + +amdgpu_restore_umd_profile_pstate_after_runpm (adev); adev->in_runpm = false;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 192/261 drm/amdgpu: cap GTT size to physical RAM on APUs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Harkirat Gill <[email protected]>
commit 5e70f6804b4d6256058c360b10e044ee04ea4a4e upstream.
On APUs, the GTT pool is backed by system RAM, but its size is not bound to the non-carveout memory that actually backs it. A user can end up with GTT + VRAM exceeding total physical memory through the following sequence:
- Have a large non-carveout memory space (~128GB) and accordingly set a large GTT (~100GB) via the ttm module parameter.
- Lower the non-carveout memory space in BIOS by increasing the UMA Frame Buffer Size (VRAM) to 64GB.
- The previously set GTT value (~100GB) persists, even though the new non-carveout space (64GB) can no longer back it.
This leads to a case where kernel reports GTT (100GB) + VRAM (64GB) despite the sum being greater than total physical memory (128GB).
Cap the GTT size to totalram_pages() on APUs. totalram_pages() already excludes the VRAM carveout, so the resulting GTT can never exceed the system RAM that actually backs it.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +12 -0
@@ -1976,6 +1976,18 @@ int amdgpu_ttm_init(struct amdgpu_deviceelse gtt_size = (uint64_t )amdgpu_gtt_size <<20 ; +/* Cap GTT so that it does not exceed total physical RAM. */ +if (adev->flags &AMD_IS_APU ) { +u64 phys_ram = (u64 )totalram_pages () <<PAGE_SHIFT ; + +if (gtt_size > phys_ram) { + gtt_size = phys_ram; +dev_info (adev->dev, +"Capping GTT to %uM to not exceed available system memory\n" , + (unsigned int )(gtt_size / (1024 *1024 ))); + } + } +/* Initialize GTT memory pool */ r =amdgpu_gtt_mgr_init (adev, gtt_size);if (r) {
Greg Kroah-Hartman
maintainerPATCH 193/261 drm/amdkfd: Fix missing authorization check in KFD_IOC_DBG_TRAP_DISABLE
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gang Ba <[email protected]>
commit 99b2fe4f19e3be0a8d0a0b5ea98d855970889653 upstream.
Prevent unauthorized termination of active GPU debug sessions. Previously, users with /dev/kfd access could terminate another process's debug session without proper ownership or ptrace authorization.
--- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +7 -3
@@ -2977,10 +2977,14 @@ static int kfd_ioctl_set_debug_trap(stru goto out; } -/* Check if target is still PTRACED. */ +/* +* Verify debugger has permission to debug target process. +* For cross-process debugging, require active ptrace relationship. +* This applies to ALL operations to prevent unauthorized interference. +*/ rcu_read_lock (); -if (target != p && args->op !=KFD_IOC_DBG_TRAP_DISABLE - &&ptrace_parent (target->lead_thread) != current) { +if (target != p &&ptrace_parent (target->lead_thread) != current + && target->debugger_process != p) {pr_err ("PID %i is not PTRACED and cannot be debugged\n" , args->pid); r = -EPERM ; }
Greg Kroah-Hartman
maintainerPATCH 194/261 drm/amdkfd: fix QID bit leak in pqm_create_queue()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vladimir Marioukhine <[email protected]>
commit 38b73293f38658a4685ffcea666462024f858ad9 upstream.
When MES is enabled and amdgpu_amdkfd_alloc_kernel_mem() fails during the first queue creation for a process, pqm_create_queue() returns early via 'return retval' without going through the err_create_queue cleanup label.
This means clear_bit(*qid, pqm->queue_slot_bitmap) is never called, leaving the reserved QID bit permanently set in queue_slot_bitmap. Over time this leaks QID slots, potentially exhausting all available queue slots.
Fix this by replacing 'return retval' with 'goto err_allocate_pqn' so that clear_bit() is always called on the error path without touching the uninitialized pqn pointer.
AILIKFD-813
--- drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +1 -1
@@ -344,7 +344,7 @@ int pqm_create_queue(struct process_queu false);if (retval) {dev_err (dev->adev->dev,"failed to allocate process context bo\n" ); -return retval; + goto err_allocate_pqn; }memset (pdd->proc_ctx_cpu_ptr,0 ,AMDGPU_MES_PROC_CTX_SIZE ); }
Greg Kroah-Hartman
maintainerPATCH 195/261 drm/amdkfd: Handle invalid event type in CRIU event restore
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Francis <[email protected]>
commit a9cdc85839e4fe2c760aa4ca6cc341c31ad1918a upstream.
In kfd_criu_restore_event, there was no handling for the event priv data having an invalid event type. The priv data here is untrusted and can be invalid.
In that case, fail with EINVAL.
--- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/gpu/drm/amd/amdkfd/kfd_events.c +3 -0
@@ -520,6 +520,9 @@ int kfd_criu_restore_event(struct file * ret =create_other_event (p, ev, &ev_priv->event_id);break ; +default : + ret = -EINVAL ; +break ; }mutex_unlock (&p->event_mutex);
Greg Kroah-Hartman
maintainerPATCH 196/261 drm/amdkfd: hold event_mutex while checkpointing CRIU events
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: William Palacek <[email protected]>
commit ff8bc5a68a9a70bdc38d61a72c7a49c56063f9d2 upstream.
kfd_criu_checkpoint_events() counts the entries in p->event_idr via kfd_get_num_events(), allocates an array sized to that count, and then walks the same IDR to fill it. Neither the count nor the walk holds p->event_mutex.
The CRIU checkpoint caller holds only p->mutex. Event create and destroy (kfd_event_create()/kfd_event_destroy()) take p->event_mutex and do not take p->mutex, so a second thread in the same process can insert or remove events between the count and the walk. If an event is inserted, the walk iterates more entries than were counted and writes past the end of the ev_privs allocation; if an event is removed, the walk dereferences an entry that is being freed.
Hold p->event_mutex across the count and the walk so both observe a consistent view of p->event_idr. The lock is released before copy_to_user(), which only touches the local buffer. The caller already holds p->mutex and the create/destroy paths never take p->mutex, so the p->mutex -> p->event_mutex order is not inverted and no deadlock is introduced.
--- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-)
drivers/gpu/drm/amd/amdkfd/kfd_events.c +18 -4
@@ -544,15 +544,27 @@ int kfd_criu_checkpoint_events(struct kfint ret =0 ;struct kfd_event *ev;uint32_t ev_id; +uint32_t num_events; -uint32_t num_events =kfd_get_num_events (p); - -if (!num_events) +/* Serialize the count and the walk below against concurrent event +* create/destroy. Those paths take only p->event_mutex, not the +* p->mutex held by the CRIU checkpoint caller, so without this the +* event_idr can grow between kfd_get_num_events() and the loop and the +* walk writes past the ev_privs allocation. +*/ +mutex_lock (&p->event_mutex); + + num_events =kfd_get_num_events (p); +if (!num_events) { +mutex_unlock (&p->event_mutex);return 0 ; + } ev_privs =kvzalloc (num_events *sizeof (*ev_privs),GFP_KERNEL ); -if (!ev_privs) +if (!ev_privs) { +mutex_unlock (&p->event_mutex);return -ENOMEM ; + }idr_for_each_entry (&p->event_idr, ev, ev_id) {
@@ -593,6 +605,8 @@ int kfd_criu_checkpoint_events(struct kf i++; } +mutex_unlock (&p->event_mutex); + ret =copy_to_user (user_priv_data + *priv_data_offset, ev_privs, num_events *sizeof (*ev_privs));if (ret) {
Greg Kroah-Hartman
maintainerPATCH 197/261 drm/vmwgfx: fix guest_memory_dirty bitfield clobbered as size
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 83195b778f2d109a3a4f3ffaba4dce7e4cdb58aa upstream.
Two sites in vmwgfx_resource.c assign boolean literals to res->guest_memory_size, which is an unsigned long allocation-size field; the intended target is the adjacent res->guest_memory_dirty bitfield. After the assignments the field holds 0 or 1 instead of the resource's MOB allocation size:
- vmw_resource_release() writes 0 (false), and
- vmw_resource_unbind_list() writes 1 (true).
Subsequent revalidation paths read guest_memory_size when computing the dirty page range (vmw_bo_dirty_transfer_to_res()) and the buffer allocation size (vmw_resource_buf_alloc()), producing zero-length walks or wrap-around ranges that read or write past the MOB bitmap. The dirty-tracking intent of the original code (mark the resource as dirtied since the last sync) is also lost, since guest_memory_dirty is never updated.
Rename both assignments to guest_memory_dirty.
--- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +2 -2
@@ -134,7 +134,7 @@ static void vmw_resource_release(struct val_buf.num_shared =0 ; res->func->unbind (res, false, &val_buf); } - res->guest_memory_size = false; + res->guest_memory_dirty = false;vmw_resource_mob_detach (res);if (res->dirty) res->func->dirty_free (res);
@@ -764,7 +764,7 @@ void vmw_resource_unbind_list(struct vmwif (!WARN_ON_ONCE (!res->func->unbind)) (void ) res->func->unbind (res, res->res_dirty, &val_buf); - res->guest_memory_size = true; + res->guest_memory_dirty = true; res->res_dirty = false;vmw_resource_mob_detach (res); }
Greg Kroah-Hartman
maintainerPATCH 198/261 drm/vmwgfx: reject DX_BIND_QUERY without a DX context
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 55ec09c9ce10b1272802c7ab6c1be2ea0dbc68db upstream.
vmw_cmd_dx_bind_query() unconditionally dereferences sw_context->dx_ctx_node->ctx. Userspace can trigger a NULL pointer dereference from any render-node fd by submitting an execbuf with dx_context_handle == SVGA3D_INVALID_ID and a SVGA_3D_CMD_DX_BIND_QUERY opcode in the command stream: dx_ctx_node is left NULL and the kernel oopses on the assignment. The same NULL is then re-read in vmw_resources_reserve() via vmw_context_get_dx_query_mob().
All sibling DX handlers fail-close on a missing dx_ctx_node using VMW_GET_CTX_NODE(). Use the same pattern here, returning -EINVAL up front before any relocation state is published.
--- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +5 -1
@@ -1289,9 +1289,13 @@ static int vmw_cmd_dx_bind_query(struct SVGA3dCmdHeader *header) {VMW_DECLARE_CMD_VAR (*cmd, SVGA3dCmdDXBindQuery); +struct vmw_ctx_validation_info *ctx_node =VMW_GET_CTX_NODE (sw_context);struct vmw_bo *vmw_bo;int ret; +if (!ctx_node) +return -EINVAL ; + cmd =container_of (header,typeof (*cmd), header);/*
@@ -1305,7 +1309,7 @@ static int vmw_cmd_dx_bind_query(structreturn ret; sw_context->dx_query_mob = vmw_bo; - sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx; + sw_context->dx_query_ctx = ctx_node->ctx;return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 199/261 drm/vmwgfx: drop dma_buf reference on foreign-fd prime import
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit f739416dc555fa205a785e5135d73fa39b26f35d upstream.
ttm_prime_fd_to_handle() returns -ENOSYS when the imported fd's dma_buf->ops do not match the ttm_object_device's ops, but does so without releasing the reference acquired by dma_buf_get(). Any unprivileged renderD client passing a non-vmwgfx prime fd through the DRM_VMW_GB_SURFACE_REF{,_EXT} path leaks one dma_buf reference per call and indefinitely pins the foreign exporter's GEM resources.
Funnel the error path through the existing dma_buf_put() so the reference is always dropped.
--- drivers/gpu/drm/vmwgfx/ttm_object.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/vmwgfx/ttm_object.c +5 -2
@@ -551,14 +551,17 @@ int ttm_prime_fd_to_handle(struct ttm_obif (IS_ERR (dma_buf))return PTR_ERR (dma_buf); -if (dma_buf->ops != &tdev->ops) -return -ENOSYS ; +if (dma_buf->ops != &tdev->ops) { + ret = -ENOSYS ; + goto out; + } prime = (struct ttm_prime_object *) dma_buf->priv; base = &prime->base; *handle = base->handle; ret =ttm_ref_object_add (tfile, base,NULL , false); +out:dma_buf_put (dma_buf);return ret;
Greg Kroah-Hartman
maintainerPATCH 200/261 drm/vmwgfx: validate DRAW_PRIMITIVES header size before division
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 85891d174707d8bddcec7a888fb4e1d17def34f3 upstream.
vmw_cmd_draw() computes
maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl);
where header->size is u32 and is taken straight from the user-supplied command stream. When header->size is less than sizeof(cmd->body) the unsigned subtraction wraps to nearly 4 GiB, producing a huge maxnum. Any user-controlled cmd->body.numVertexDecls then passes the bound and the loop dereferences decl[i] far past the end of the kernel command bounce buffer, producing an out-of-bounds read of kernel memory.
Reject undersized headers up front.
--- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +7 -1
@@ -1588,11 +1588,17 @@ static int vmw_cmd_draw(struct vmw_privauint32_t maxnum;int ret; + cmd =container_of (header,typeof (*cmd), header); + +if (unlikely (header->size <sizeof (cmd->body))) { +VMW_DEBUG_USER ("Illegal DRAW_PRIMITIVES header size.\n" ); +return -EINVAL ; + } + ret =vmw_cmd_cid_check (dev_priv, sw_context, header);if (unlikely (ret !=0 ))return ret; - cmd =container_of (header,typeof (*cmd), header); maxnum = (header->size -sizeof (cmd->body)) /sizeof (*decl);if (unlikely (cmd->body.numVertexDecls > maxnum)) {
Greg Kroah-Hartman
maintainerPATCH 201/261 drm/vmwgfx: bound DMA command body size against suffix pointer
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit f4f1db96bfd68b81053693ba53405b6f510ac16c upstream.
vmw_cmd_dma() locates the DMA suffix at
(unsigned long) &cmd->body + header->size - sizeof(*suffix)
without checking that header->size is large enough to contain both cmd->body and the suffix. An undersized header makes the suffix pointer underflow back into the previous command in the bounce buffer. The verifier later writes suffix->maximumOffset, clobbering verified fields of an already-relocated earlier command -- a TOCTOU on the device-visible command stream that lets one command rewrite another's GMR id, surface id, or other authenticated fields.
Reject the command if the body is too small for the suffix to fit.
--- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +6 -0
@@ -1527,6 +1527,12 @@ static int vmw_cmd_dma(struct vmw_privatbool dirty; cmd =container_of (header,typeof (*cmd), header); + +if (unlikely (header->size <sizeof (cmd->body) +sizeof (*suffix))) { +VMW_DEBUG_USER ("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n" ); +return -EINVAL ; + } + suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long ) &cmd->body + header->size -sizeof (*suffix));
Greg Kroah-Hartman
maintainerPATCH 202/261 drm/vmwgfx: use check_add_overflow for shader size+offset bound
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 54d56d5b42d2e4c72ba6e365e9774da90698aa22 upstream.
vmw_shader_define() validates the user-supplied shader window against its backing buffer with
(u64)buffer->tbo.base.size < (u64)size + (u64)offset
drm_vmw_shader_create_arg::offset is __u64 in the uapi; when it is near U64_MAX the unsigned addition wraps and the resulting tiny value passes the check. The unbounded offset is then stored in res->guest_memory_offset and forwarded to host SVGA shader-create commands.
Use check_add_overflow() to detect the wrap and compare the resulting endpoint against the buffer size.
--- drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +9 -4
@@ -25,6 +25,8 @@* **************************************************************************/ +#include <linux/overflow.h> +#include <drm/ttm/ttm_placement.h> #include "vmwgfx_binding.h"
@@ -685,7 +687,7 @@ int vmw_shader_destroy_ioctl(struct drm_ static int vmw_user_shader_alloc(struct vmw_private *dev_priv, struct vmw_bo *buffer, size_t shader_size, - size_t offset, + u64 offset, SVGA3dShaderType shader_type, uint8_t num_input_sig, uint8_t num_output_sig,
@@ -739,7 +741,7 @@ out:static struct vmw_resource *vmw_shader_alloc (struct vmw_private *dev_priv,struct vmw_bo *buffer,size_t shader_size, -size_t offset, +u64 offset,SVGA3dShaderType shader_type) {struct vmw_shader *shader;
@@ -768,7 +770,7 @@ out_err:static int vmw_shader_define (struct drm_device *dev,struct drm_file *file_priv,enum drm_vmw_shader_type shader_type_drm, -u32 buffer_handle,size_t size,size_t offset, +u32 buffer_handle,size_t size,u64 offset,uint8_t num_input_sig,uint8_t num_output_sig,uint32_t *shader_handle) {
@@ -779,13 +781,16 @@ static int vmw_shader_define(struct drm_int ret;if (buffer_handle !=SVGA3D_INVALID_ID ) { +u64 end; + ret =vmw_user_bo_lookup (file_priv, buffer_handle, &buffer);if (unlikely (ret !=0 )) {VMW_DEBUG_USER ("Couldn't find buffer for shader creation.\n" );return ret; } -if ((u64 )buffer->tbo.base.size < (u64 )size + (u64 )offset) { +if (check_add_overflow ((u64 )size, (u64 )offset, &end) || + end > buffer->tbo.base.size) {VMW_DEBUG_USER ("Illegal buffer- or shader size.\n" ); ret = -EINVAL ; goto out_bad_arg;
Greg Kroah-Hartman
maintainerPATCH 203/261 drm/vmwgfx: validate external BO copy bounds for both stride paths
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zack Rusin <[email protected]>
commit 706c93c5813caabbb0d0a576c017d15aeec2c113 upstream.
vmw_external_bo_copy() trusts caller-supplied offsets, strides, and heights and operates on imported dma-buf vmaps:
- The equal-stride memcpy() bound was clamped after subtracting the offsets from dst_size and src_size; an offset larger than the BO size wraps the unsigned subtraction to a huge value and the resulting memcpy() runs off the end of the vmap. dst_stride * height is also a u32 multiplication that can overflow.
- The non-equal-stride row-by-row path had no bound at all. The loop touches bytes through offset + (height - 1) * stride + width_in_bytes, with only a WARN_ON(dst_stride < width_in_bytes), and could likewise step past the end of either mapping.
The offsets and strides are derived from STDU/SOU plane state, so a configured CRTC submitting a crafted atomic commit on an imported framebuffer can reach this path.
Validate the exact row-copy endpoint against each BO's size up front using check_mul_overflow() and check_add_overflow(). Use the bulk memcpy() path only when width_in_bytes covers the whole stride; otherwise copy one row at a time so partial-row updates near the bottom of a framebuffer remain valid. Also reject zero strides and stride < width_in_bytes, both of which the row-by-row path cannot represent safely.
--- drivers/gpu/drm/vmwgfx/vmwgfx_blit.c | 39 +++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_blit.c +31 -8
@@ -30,6 +30,7 @@#include "vmwgfx_bo.h" #include <linux/highmem.h> +#include <linux/overflow.h> /* * Template that implements find_first_diff() for a generic
@@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct v container_of(dst->tbo.bdev, struct vmw_private, bdev);size_t dst_size = dst->tbo.resource->size;size_t src_size = src->tbo.resource->size; +size_t dst_end, src_end;struct iosys_map dst_map = {0 };struct iosys_map src_map = {0 }; +bool dst_mapped = false; +bool src_mapped = false;int ret, i;int x_in_bytes;u8 *vsrc;u8 *vdst; +if (!height || !width_in_bytes) +return 0 ; + +if (!dst_stride || !src_stride) +return -EINVAL ; +if (dst_stride < width_in_bytes || src_stride < width_in_bytes) +return -EINVAL ; +if (check_mul_overflow ((size_t )dst_stride, (size_t )height -1 , &dst_end) || +check_add_overflow (dst_end, (size_t )width_in_bytes, &dst_end) || +check_add_overflow ((size_t )dst_offset, dst_end, &dst_end) || + dst_end > dst_size || +check_mul_overflow ((size_t )src_stride, (size_t )height -1 , &src_end) || +check_add_overflow (src_end, (size_t )width_in_bytes, &src_end) || +check_add_overflow ((size_t )src_offset, src_end, &src_end) || + src_end > src_size) { +drm_dbg_driver (&vmw->drm,"Out-of-bounds external BO copy\n" ); +return -EINVAL ; + } + vsrc =map_external (src, &src_map);if (!vsrc) {drm_dbg_driver (&vmw->drm,"Wasn't able to map src\n" ); ret = -ENOMEM ; goto out; } + src_mapped = true; vdst =map_external (dst, &dst_map);if (!vdst) {
@@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct v ret = -ENOMEM ; goto out; } + dst_mapped = true; vsrc += src_offset; vdst += dst_offset; -if (src_stride == dst_stride) { - dst_size -= dst_offset; - src_size -= src_offset; -memcpy (vdst, vsrc, -min (dst_stride * height,min (dst_size, src_size))); +if (src_stride == dst_stride && width_in_bytes == dst_stride) { +memcpy (vdst, vsrc, dst_stride * (size_t )height); }else { -WARN_ON (dst_stride < width_in_bytes);for (i =0 ; i < height; ++i) {memcpy (vdst, vsrc, width_in_bytes); vsrc += src_stride;
@@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct v ret =0 ; out: -unmap_external (src, &src_map); -unmap_external (dst, &dst_map); +if (src_mapped) +unmap_external (src, &src_map); +if (dst_mapped) +unmap_external (dst, &dst_map);return ret; }
Greg Kroah-Hartman
maintainerPATCH 204/261 spi: spi-cadence: enable SPI_CONTROLLER_MUST_TX
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Guo <[email protected]>
commit f6b625639e39bc384a7bddbf134a698d40258b3b upstream.
During an SPI read operation, even if the xspi->txbuf passed to the cdns_spi_writerinterface is empty, it is still necessary to call cdns_spi_write(xspi, CDNS_SPI_TXD, txw); otherwise, the read operation will fail to obtain data correctly due to a lack of clocks.
--- drivers/spi/spi-cadence.c | 1 + 1 file changed, 1 insertion(+)
drivers/spi/spi-cadence.c +1 -0
@@ -722,6 +722,7 @@ static int cdns_spi_probe(struct platfor ctlr->unprepare_transfer_hardware = cdns_unprepare_transfer_hardware; ctlr->mode_bits =SPI_CPOL |SPI_CPHA ; ctlr->bits_per_word_mask =SPI_BPW_MASK (8 ); + ctlr->flags =SPI_CONTROLLER_MUST_TX ;if (of_device_is_compatible (pdev->dev.of_node,"cix,sky1-spi-r1p6" )) ctlr->bits_per_word_mask |=SPI_BPW_MASK (16 ) |SPI_BPW_MASK (32 );
Greg Kroah-Hartman
maintainerPATCH 205/261 HID: logitech-dj: Fix maxfield check in DJ short report validation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
commit 590cc4d782487632a52f37c2171bee1eeea29627 upstream.
Commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") added validation for the DJ short output report, but the error path dereferences rep->field[0] even when rep->maxfield is zero.
Commit 8b9a097eb2fc ("HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report") made the check conditional on rep being present, but a crafted descriptor can still create report ID 0x20 with only padding output items. hid-core registers the report, ignores the padding field, and leaves rep->maxfield as zero.
In that case the validation enters the rep->maxfield < 1 branch and then dereferences rep->field[0]->report_count while printing the error message, causing a NULL pointer dereference during probe. This is reproducible with uhid by emulating a Logitech receiver with a padding-only DJ short output report:
BUG: KASAN: null-ptr-deref in logi_dj_probe+0xb1/0x754 [hid_logitech_dj] Read of size 4 at addr 0000000000000028 by task kworker/4:1/129 ... Call Trace: logi_dj_probe+0xb1/0x754 [hid_logitech_dj] hid_device_probe+0x329/0x3f0 [hid] really_probe+0x162/0x570 __device_attach+0x137/0x2c0 bus_probe_device+0x38/0xc0 device_add+0xa56/0xce0 hid_add_device+0x19c/0x280 [hid] uhid_device_add_worker+0x2c/0xb0 [uhid]
Reject the zero-field report before printing the field report_count.
--- drivers/hid/hid-logitech-dj.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/hid/hid-logitech-dj.c +7 -2
@@ -1797,8 +1797,13 @@ static int logi_dj_probe(struct hid_devi output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT ]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT ]; -if (rep && (rep->maxfield <1 || - rep->field[0 ]->report_count !=DJREPORT_SHORT_LENGTH -1 )) { +if (rep && rep->maxfield <1 ) { +hid_err (hdev,"Expected size of DJ short report is %d, but got 0" , +DJREPORT_SHORT_LENGTH -1 ); +return -EINVAL ; + } + +if (rep && rep->field[0 ]->report_count !=DJREPORT_SHORT_LENGTH -1 ) {hid_err (hdev,"Expected size of DJ short report is %d, but got %d" ,DJREPORT_SHORT_LENGTH -1 , rep->field[0 ]->report_count);return -EINVAL ;
Greg Kroah-Hartman
maintainerPATCH 206/261 ata: libahci_platform: Do not set mask_port_map when not needed
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Damien Le Moal <[email protected]>
commit 2c202e6c4f4dd19d2e8c1dfac9df05170aa3934f upstream.
Commit 8c87215dd3a2 ("ata: libahci_platform: support non-consecutive port numbers") modified ahci_platform_get_resources() to allow identifying the ports of a controller that are defined as child nodes of the controller node in order to support non-consecutive port numbers (as defined by the platform device tree).
However, this commit also erroneously sets bit 0 of hpriv->mask_port_map when the platform devices tree does not define port child nodes, to match the fact that the temporary default number of ports used in that case is 1 (which is also consistent with the fact that only index 0 of hpriv->phys[] is initialized with the call to ahci_platform_get_phy(). But doing so causes ahci_platform_init_host() to initialize and probe only the first port, even if this function determines that the controller has in fact multiple ports using the capability register of the controller (through a call to ahci_nr_ports()). This can be seen with the ahci_mvebu driver (Armada 385 SoC) with the second port declared as "dummy":
ahci-mvebu f10a8000.sata: masking port_map 0x3 -> 0x1
ahci-mvebu f10a8000.sata: AHCI vers 0001.0000, 32 command slots, 6 Gbps, platform mode ahci-mvebu f10a8000.sata: 1/2 ports implemented (port mask 0x1) ahci-mvebu f10a8000.sata: flags: 64bit ncq sntf led only pmp fbs pio slum part sxs scsi host0: ahci-mvebu
scsi host1: ahci-mvebu
ata1: SATA max UDMA/133 mmio [mem 0xf10a8000-0xf10a9fff] port 0x100 irq 40 lpm-pol 0
ata2: DUMMY
Fix this issue by removing setting bit 0 of hpriv->mask_port_map when the platform device tree does not define port child nodes.
--- drivers/ata/libahci_platform.c | 2 -- 1 file changed, 2 deletions(-)
drivers/ata/libahci_platform.c +0 -2
@@ -660,8 +660,6 @@ struct ahci_host_priv *ahci_platform_get* If no sub-node was found, keep this for device tree * compatibility */ - hpriv->mask_port_map |=BIT (0 ); - rc =ahci_platform_get_phy (hpriv,0 , dev, dev->of_node);if (rc) goto err_out;
Greg Kroah-Hartman
maintainerPATCH 207/261 ata: ahci: Make ahci_ignore_port() handle empty mask_port_map
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Cassel <[email protected]>
commit 130ff5c8b78e6fd05270a04985c50bce6a3de6c1 upstream.
Commit 8c87215dd3a2 ("ata: libahci_platform: support non-consecutive port numbers") added a skip to ahci_platform_enable_phys() for ports that are not in mask_port_map.
The code in ahci_platform_get_resources(), will currently set mask_port_map for each child "port" node it finds in the device tree.
However, device trees that do not have any child "port" nodes will not have mask_port_map set, and for non-device tree platforms mask_port_map will only exist as a quirk for specific PCI device + vendor IDs, or as a kernel module parameter, but will not be set by default.
Therefore, the common thing is that mask_port_map is only set if you do not want to use all ports (as defined by Offset 0Ch: PI – Ports Implemented register), but instead only want to use the ports in mask_port_map. If mask_port_map is not set, all ports are available.
Thus, ahci_ignore_port() must be able to handle an empty mask_port_map.
--- drivers/ata/ahci.h | 8 ++++++-- drivers/ata/libahci.c | 1 + 2 files changed, 7 insertions(+), 2 deletions(-)
drivers/ata/ahci.h +6 -2
@@ -388,8 +388,12 @@ struct ahci_host_priv {static inline bool ahci_ignore_port (struct ahci_host_priv *hpriv,unsigned int portid) { -return portid >= hpriv->nports || - !(hpriv->mask_port_map & (1 << portid)); +if (portid >= hpriv->nports) +return true; +/* mask_port_map not set means that all ports are available */ +if (!hpriv->mask_port_map) +return false; +return !(hpriv->mask_port_map & (1 << portid)); }extern int ahci_ignore_sss;
drivers/ata/libahci.c +1 -0
@@ -541,6 +541,7 @@ void ahci_save_initial_config(struct dev hpriv->saved_port_map = port_map; } +/* mask_port_map not set means that all ports are available */ if (hpriv->mask_port_map) { dev_warn(dev, "masking port_map 0x%lx -> 0x%lx\n", port_map,
Greg Kroah-Hartman
maintainerPATCH 208/261 iommu/sva: move x86 disable check before allocation
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wei Yang <[email protected]>
Backport of commit 72f98ef9a4be ("iommu: disable SVA when CONFIG_X86 is set") placed the IS_ENABLED(CONFIG_X86) early-return in iommu_sva_bind_device() after iommu_sva_alloc_pasid() and kzalloc(handle), while upstream puts it at the function start.
On x86 this leaks the kzalloc'd struct iommu_sva (early return skips kfree) and a globally allocated PASID (mm->pasid wrongly set, never unbound). Move the check before any allocation/side effect.
--- drivers/iommu/iommu-sva.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/iommu/iommu-sva.c +3 -3
@@ -62,6 +62,9 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mmstruct iommu_sva *handle;int ret; +if (IS_ENABLED (CONFIG_X86 )) +return ERR_PTR (-EOPNOTSUPP ); +/* Allocate mm->pasid if necessary. */ ret =iommu_sva_alloc_pasid (mm, dev);if (ret)
@@ -71,9 +74,6 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mmif (!handle)return ERR_PTR (-ENOMEM ); -if (IS_ENABLED (CONFIG_X86 )) -return ERR_PTR (-EOPNOTSUPP ); -mutex_lock (&iommu_sva_lock);/* Search for an existing domain. */ domain = iommu_get_domain_for_dev_pasid(dev, mm->pasid,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 209/261 mm/hugetlb: fix swap entry corruption when clearing uffd-wp at fork()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kiryl Shutsemau (Meta) <[email protected]>
[ Upstream commit 83abe2fd5b3aeb3123b5408a5a91709c5538fb23 ]
copy_hugetlb_page_range() clears the uffd-wp bit of migration and hwpoison entries with huge_pte_clear_uffd_wp(), which operates on the present-PTE bit position. Swap entries keep the uffd-wp state elsewhere -- the migration branch reads and sets it with pte_swp_uffd_wp() and pte_swp_mkuffd_wp() -- and the present-PTE position falls into the swap payload. On x86-64 it lands in the inverted swap offset, where a naturally-aligned hugetlb PFN always has the affected bit set, so the clear advances the encoded PFN by two pages.
No userfaultfd needs to be involved: the clear is guarded only by the child VMA not being uffd-wp registered, so a plain fork() with an in-flight hugetlb migration entry (or a poisoned hugetlb page) corrupts the entry copied into the child. Instrumenting the clear and forking after MADV_HWPOISON on a 2MB anon hugetlb page shows:
offset before=120e00
offset after =120e02
The fallout is mostly latent: rmap walks match migration entries by folio range and remove_migration_pte() rebuilds the PTE from the folio, so a within-folio PFN skew heals once migration completes. But any path that re-encodes the corrupted offset -- e.g. hugetlb_change_protection() rewriting a writable migration entry via make_readable_migration_entry(swp_offset(entry)) -- propagates it.
Migration entries legitimately carry uffd-wp, so clear it with pte_swp_clear_uffd_wp(), matching copy_nonpresent_pte() and move_huge_pte().
A hwpoison entry, on the other hand, never carries the uffd-wp bit: it is installed fresh by make_hwpoison_entry() (try_to_unmap_one() does not preserve uffd-wp on the hwpoison path) and hugetlb_change_protection() leaves hwpoison entries untouched. There was nothing to clear there, only the corruption, so drop the clear entirely.
--- mm/hugetlb.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
mm/hugetlb.c +7 -3
@@ -5112,8 +5112,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,*/ ; }else if (unlikely (is_hugetlb_entry_hwpoisoned (entry))) { -if (!userfaultfd_wp (dst_vma)) - entry =huge_pte_clear_uffd_wp (entry); +/* +* A hwpoison entry never carries the uffd-wp bit: it is +* installed fresh by make_hwpoison_entry() and +* hugetlb_change_protection() leaves it untouched, so +* there is nothing to clear for the child. +*/ set_huge_pte_at (dst, addr, dst_pte, entry, sz); }else if (unlikely (is_hugetlb_entry_migration (entry))) {swp_entry_t swp_entry =pte_to_swp_entry (entry);
@@ -5132,7 +5136,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,set_huge_pte_at (src, addr, src_pte, entry, sz); }if (!userfaultfd_wp (dst_vma)) - entry =huge_pte_clear_uffd_wp (entry); + entry =pte_swp_clear_uffd_wp (entry);set_huge_pte_at (dst, addr, dst_pte, entry, sz); }else if (unlikely (is_pte_marker (entry))) { pte_marker marker = copy_pte_marker(
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 210/261 mm/huge_memory: unlock i_mmap_rwsem before releasing after-split folios
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kiryl Shutsemau (Meta) <[email protected]>
[ Upstream commit e923bd21058ea02fd0dcd3549d151d143fd036e5 ]
__folio_split() keeps dereferencing the mapping after the split:shmem_uncharge(mapping->host) and remap_page() while the folios are still frozen/locked, and i_mmap_unlock_read(mapping) at the very end, after the after-split folios have been unlocked and freed.
Nothing holds an inode reference across that. The split relies on @folio -- which the beyond-EOF drop loop never removes, as it starts at folio_next(folio) -- staying locked and in the page cache to hold off eviction. But the unlock loop unlocks @folio before i_mmap_unlock_read() runs. If the caller's @lock_at is a tail beyond EOF, as memory_failure() passes when splitting a poisoned tail of a shmem THP that reaches past i_size during truncation, it too is gone from the page cache; so once @folio is unlocked no locked, in-cache folio pins the inode, and a concurrent final iput() can evict and RCU-free it before i_mmap_unlock_read() touches i_mmap_rwsem:
BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790 i_mmap_unlock_read include/linux/fs.h:537 [inline] __folio_split+0x732/0x1640 mm/huge_memory.c:4100 try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675 memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470 Freed by task 4601: shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177 evict+0x57f/0xac0 fs/inode.c:870
Do every mapping dereference while @folio still pins the inode: drop i_mmap_rwsem right after remap_page(), before the loop that unlocks and frees the after-split folios, and clear @mapping so the exit path does not unlock it again. shmem_uncharge() and remap_page() already run before that point, so after this nothing past the unlock loop touches the inode or the mapping.
This is now a rule the split depends on, alongside keeping @folio frozen until the page cache is updated: no inode or mapping dereference once the after-split folios start being unlocked.
(cherry picked from commit e923bd21058ea02fd0dcd3549d151d143fd036e5) [ kas: adapt to the __split_huge_page()/split_huge_page_to_list()
two-function split: pass @mapping into __split_huge_page() and drop it there, before the loop that frees the after-split subpages while the head is still locked; the caller then skips its own i_mmap unlock ]
--- mm/huge_memory.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
mm/huge_memory.c +14 -2
@@ -2489,7 +2489,7 @@ static void __split_huge_page_tail(struct folio *folio, int tail, }static void __split_huge_page (struct page *page,struct list_head *list, -pgoff_t end) +pgoff_t end,struct address_space *mapping) {struct folio *folio =page_folio (page);struct page *head = &folio->page;
@@ -2564,6 +2564,16 @@ static void __split_huge_page(struct page *page, struct list_head *list,if (folio_test_swapcache (folio))split_swap_cluster (folio->swap); +/* +* Drop the mapping while the head page is still locked and thus pins +* the inode. The loop below may free the after-split subpages -- +* including the head, when @page is a tail beyond EOF that the split +* dropped from the page cache -- which could otherwise let the inode, +* and @mapping, be freed before this unlock. +*/ +if (mapping) +i_mmap_unlock_read (mapping); +for (i =0 ; i < nr; i++) {struct page *subpage = head + i;if (subpage == page)
@@ -2745,7 +2755,9 @@ int split_huge_page_to_list(struct page *page, struct list_head *list) } } -__split_huge_page (page, list, end); +__split_huge_page (page, list, end, mapping); +/* __split_huge_page() dropped the i_mmap lock */ + mapping =NULL ; ret =0 ; }else {spin_unlock (&ds_queue->split_queue_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 212/261 gpio: pch: use raw_spinlock_t for the register lock
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Junjie Cao <[email protected]>
[ Upstream commit a02b8950d619123da64f69b70fe1dadef217dfe4 ]
pch_irq_type() is registered as the irq_chip .irq_set_type callback and takes chip->spinlock with spin_lock_irqsave(). This callback is reached from __setup_irq() -> __irq_set_trigger() -> chip->irq_set_type() while the caller holds desc->lock, a raw_spinlock_t, with hardirqs disabled. That context is not sleepable, but on PREEMPT_RT a regular spinlock_t is an rtmutex-backed sleeping lock, so acquiring it there is invalid.
This was confirmed on a PREEMPT_RT kernel with lockdep (PROVE_RAW_LOCK_NESTING and DEBUG_ATOMIC_SLEEP). A grounded PoC mirrored pch_irq_type()'s locking and drove it through the real genirq carrier irq_set_irq_type() -> __irq_set_trigger() -> chip->irq_set_type(), i.e. the same __irq_set_trigger() edge that __setup_irq() takes for a requested IRQ. With the original spin_lock_irqsave() edge lockdep reported an invalid wait context, immediately followed by:
BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 95, name: insmod hardirqs last disabled at (3784): _raw_spin_lock_irqsave+0x4f/0x60 rt_spin_lock+0x3a/0x1c0 repro_irq_set_type+0x64/0xa0 [pch_repro] __irq_set_trigger+0x69/0x140 irq_set_irq_type+0x78/0xd0
Switching the mirrored lock to raw_spinlock_t made both splats go away.
Convert the register lock to raw_spinlock_t. The same lock also serializes the GPIO direction/value callbacks and the suspend/resume register save/restore, but all of those critical sections only perform MMIO register accesses (ioread32()/iowrite32()) and irq_set_handler_locked(); none of them contain sleepable operations. Keeping this register lock non-sleeping is therefore appropriate for the irqchip callbacks and does not change the GPIO-side locking contract.
This is the same class of issue and fix as recently addressed for other GPIO controllers, e.g. commit 286533cb14a3 ("gpio: sch: use raw_spinlock_t in the irq startup path") and commit 90f0109019e6 ("gpio: eic-sprd: use raw_spinlock_t in the irq startup path").
--- drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
drivers/gpio/gpio-pch.c +14 -14
@@ -97,7 +97,7 @@ struct pch_gpio {struct pch_gpio_reg_data pch_gpio_reg;int irq_base;enum pch_type_t ioh; -spinlock_t spinlock; +raw_spinlock_t spinlock; };static void pch_gpio_set (struct gpio_chip *gpio,unsigned int nr,int val)
@@ -106,7 +106,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val)struct pch_gpio *chip =gpiochip_get_data (gpio);unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags); reg_val =ioread32 (&chip->reg->po);if (val) reg_val |=BIT (nr);
@@ -114,7 +114,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) reg_val &= ~BIT (nr);iowrite32 (reg_val, &chip->reg->po); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags); }static int pch_gpio_get (struct gpio_chip *gpio,unsigned int nr)
@@ -132,7 +132,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr,u32 reg_val;unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags); reg_val =ioread32 (&chip->reg->po);if (val)
@@ -146,7 +146,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, pm |=BIT (nr);iowrite32 (pm, &chip->reg->pm); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
@@ -157,12 +157,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr)u32 pm;unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags); pm =ioread32 (&chip->reg->pm); pm &=BIT (gpio_pins[chip->ioh]) -1 ; pm &= ~BIT (nr);iowrite32 (pm, &chip->reg->pm); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
@@ -264,7 +264,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type)return 0 ; } -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags);/* Set interrupt mode */ im =ioread32 (im_reg) & ~(PCH_IM_MASK << (im_pos *4 ));
@@ -276,7 +276,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type)else if (type &IRQ_TYPE_EDGE_BOTH )irq_set_handler_locked (d, handle_edge_irq); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
@@ -373,7 +373,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, chip->ioh = id->driver_data; chip->reg = chip->base;pci_set_drvdata (pdev, chip); -spin_lock_init (&chip->spinlock); +raw_spin_lock_init (&chip->spinlock);pch_gpio_setup (chip); ret =devm_gpiochip_add_data (dev, &chip->gpio, chip);
@@ -406,9 +406,9 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev)struct pch_gpio *chip =dev_get_drvdata (dev);unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags);pch_gpio_save_reg_conf (chip); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
@@ -418,11 +418,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev)struct pch_gpio *chip =dev_get_drvdata (dev);unsigned long flags; -spin_lock_irqsave (&chip->spinlock, flags); +raw_spin_lock_irqsave (&chip->spinlock, flags);iowrite32 (0x01 , &chip->reg->reset);iowrite32 (0x00 , &chip->reg->reset);pch_gpio_restore_reg_conf (chip); -spin_unlock_irqrestore (&chip->spinlock, flags); +raw_spin_unlock_irqrestore (&chip->spinlock, flags);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 213/261 Revert "ia64: Make acpi_cpufreq_cpu_exit return void"
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Frank Scheiner <[email protected]>
Commit b4b1ddc9dfe9 ("cpufreq: Make cpufreq_driver->exit() return void") was part of v6.6.145-rc1. But as it was derived from a later Linux version w/o support for ia64 it didn't touch all relevant parts in linux-6.6.y. Which now required an extra fix to build correctly for ia64. But b4b1ddc9dfe9 was then not part of the release v6.6.145. So the extra fix can go now, too.
This reverts commit bb51b626b5a8ef349b11876572540f19ad4d5030.
--- drivers/cpufreq/ia64-acpi-cpufreq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/cpufreq/ia64-acpi-cpufreq.c +3 -1
@@ -302,7 +302,7 @@ acpi_cpufreq_cpu_init ( } -static void +static int acpi_cpufreq_cpu_exit (struct cpufreq_policy *policy) {
@@ -316,6 +316,8 @@ acpi_cpufreq_cpu_exit (kfree (policy->freq_table);kfree (data); } + +return (0 ); }
Greg Kroah-Hartman
maintainerPATCH 214/261 usb: musb: omap2430: clean up probe error handling
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
[ Upstream commit 51d4b0a44c82e5eff056ef76acd2c3c605a8eb74 ]
Using numbered error labels is discouraged (e.g. as it requires renumbering them when adding a new intermediate error path).
Rename the error labels after what they do.
While at it, drop the redundant platform allocation failure dev_err() as the error would already have been logged by the allocator.
Stable-dep-of: c947360ae63e ("usb: musb: omap2430: Do not put borrowed of_node in probe")
--- drivers/usb/musb/omap2430.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-)
drivers/usb/musb/omap2430.c +10 -13
@@ -318,13 +318,11 @@ static int omap2430_probe(struct platfor glue =devm_kzalloc (&pdev->dev,sizeof (*glue),GFP_KERNEL );if (!glue) - goto err0; +return -ENOMEM ; musb =platform_device_alloc ("musb-hdrc" ,PLATFORM_DEVID_AUTO ); -if (!musb) { -dev_err (&pdev->dev,"failed to allocate musb device\n" ); - goto err0; - } +if (!musb) +return -ENOMEM ; musb->dev.parent = &pdev->dev; musb->dev.dma_mask = &omap2430_dmamask;
@@ -348,15 +346,15 @@ static int omap2430_probe(struct platfor pdata =devm_kzalloc (&pdev->dev,sizeof (*pdata),GFP_KERNEL );if (!pdata) - goto err2; + goto err_put_musb; data =devm_kzalloc (&pdev->dev,sizeof (*data),GFP_KERNEL );if (!data) - goto err2; + goto err_put_musb; config =devm_kzalloc (&pdev->dev,sizeof (*config),GFP_KERNEL );if (!config) - goto err2; + goto err_put_musb;of_property_read_u32 (np,"mode" , (u32 *)&pdata->mode); of_property_read_u32(np, "interface-type",
@@ -379,7 +377,7 @@ static int omap2430_probe(struct platforif (!control_pdev) {dev_err (&pdev->dev,"Failed to get control device\n" ); ret = -EINVAL ; - goto err2; + goto err_put_musb; } glue->control_otghs = &control_pdev->dev; }
@@ -455,22 +453,21 @@ static int omap2430_probe(struct platfor ret =platform_device_add (musb);if (ret) {dev_err (&pdev->dev,"failed to register musb device\n" ); - goto err3; + goto err_disable_rpm; }of_node_put (np);return 0 ; -err3: +err_disable_rpm:pm_runtime_disable (glue->dev); err_put_control_otghs:if (!IS_ERR (glue->control_otghs))put_device (glue->control_otghs); -err2: +err_put_musb:of_node_put (np);platform_device_put (musb); -err0:return ret; }
Greg Kroah-Hartman
maintainerPATCH 215/261 usb: musb: omap2430: Do not put borrowed of_node in probe
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit c947360ae63eee1c9eacc030dd6f5a53f717addf ]
omap2430_probe() stores pdev->dev.of_node in a local np variable. This is a borrowed pointer and the probe function does not take a reference to it.
The success and error paths nevertheless call of_node_put(np). This drops a reference that is owned by the platform device, and can leave pdev->dev.of_node with an unbalanced reference count.
Do not put the borrowed platform device node from omap2430_probe(). References taken for the child MUSB device are handled by the device core, and the ctrl-module phandle reference is still released separately.
--- drivers/usb/musb/omap2430.c | 2 -- 1 file changed, 2 deletions(-)
drivers/usb/musb/omap2430.c +0 -2
@@ -455,7 +455,6 @@ static int omap2430_probe(struct platfordev_err (&pdev->dev,"failed to register musb device\n" ); goto err_disable_rpm; } -of_node_put (np);return 0 ;
@@ -465,7 +464,6 @@ err_put_control_otghs:if (!IS_ERR (glue->control_otghs))put_device (glue->control_otghs); err_put_musb: -of_node_put (np);platform_device_put (musb);return ret;
Greg Kroah-Hartman
maintainerPATCH 216/261 drm: renesas: Move RZ/G2L MIPI DSI driver to rz-du
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lad Prabhakar <[email protected]>
[ Upstream commit 1b5dfd1881dbe303536d4167500b94549ff2f6a7 ]
All the RZ/G2L DU specific components are located under the rz-du folder, so it makes sense to move the RZ/G2L MIPI DSI driver there instead of keeping it in the rcar-du folder. This change improves the organization and modularity of the driver configuration by grouping related settings together.
Stable-dep-of: 7cbba8a8ba02 ("drm: renesas: rzg2l_mipi_dsi: Increase reset deassertion delay")
---
drivers/gpu/drm/renesas/Kconfig | 1 +
drivers/gpu/drm/renesas/Makefile | 1 +
drivers/gpu/drm/renesas/rcar-du/Kconfig | 8 --------
drivers/gpu/drm/renesas/rcar-du/Makefile | 2 --
drivers/gpu/drm/renesas/rz-du/Kconfig | 8 ++++++++
drivers/gpu/drm/renesas/rz-du/Makefile | 3 +++
.../gpu/drm/renesas/{rcar-du => rz-du}/rzg2l_mipi_dsi.c | 0
.../drm/renesas/{rcar-du => rz-du}/rzg2l_mipi_dsi_regs.h | 0
drivers/gpu/drm/renesas/Kconfig | 1
drivers/gpu/drm/renesas/Makefile | 1
drivers/gpu/drm/renesas/rcar-du/Kconfig | 8
drivers/gpu/drm/renesas/rcar-du/Makefile | 2
drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c | 816 ------------------
drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h | 151 ---
drivers/gpu/drm/renesas/rz-du/Kconfig | 8
drivers/gpu/drm/renesas/rz-du/Makefile | 3
drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 816 ++++++++++++++++++
drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h | 151 +++
10 files changed, 980 insertions(+), 977 deletions(-)
create mode 100644 drivers/gpu/drm/renesas/rz-du/Kconfig
create mode 100644 drivers/gpu/drm/renesas/rz-du/Makefile
rename drivers/gpu/drm/renesas/{rcar-du => rz-du}/rzg2l_mipi_dsi.c (100%)
rename drivers/gpu/drm/renesas/{rcar-du => rz-du}/rzg2l_mipi_dsi_regs.h (100%)drivers/gpu/drm/renesas/Kconfig +1 -0
@@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only source "drivers/gpu/drm/renesas/rcar-du/Kconfig" +source "drivers/gpu/drm/renesas/rz-du/Kconfig" source "drivers/gpu/drm/renesas/shmobile/Kconfig"
drivers/gpu/drm/renesas/Makefile +1 -0
@@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += rcar-du/ +obj-y += rz-du/ obj-$(CONFIG_DRM_SHMOBILE) += shmobile/
drivers/gpu/drm/renesas/rcar-du/Kconfig +0 -8
@@ -60,14 +60,6 @@ config DRM_RCAR_MIPI_DSI select DRM_MIPI_DSI select RESET_CONTROLLER -config DRM_RZG2L_MIPI_DSI - tristate "RZ/G2L MIPI DSI Encoder Support" - depends on DRM && DRM_BRIDGE && OF - depends on ARCH_RENESAS || COMPILE_TEST - select DRM_MIPI_DSI - help - Enable support for the RZ/G2L Display Unit embedded MIPI DSI encoders. - config DRM_RCAR_VSP bool "R-Car DU VSP Compositor Support" if ARM default y if ARM64
drivers/gpu/drm/renesas/rcar-du/Makefile +0 -2
@@ -14,5 +14,3 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-dr obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o obj-$(CONFIG_DRM_RCAR_MIPI_DSI) += rcar_mipi_dsi.o - -obj-$(CONFIG_DRM_RZG2L_MIPI_DSI) += rzg2l_mipi_dsi.o
drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi.c deleted +0 -816
816 lines not on this page · raw message
drivers/gpu/drm/renesas/rcar-du/rzg2l_mipi_dsi_regs.h deleted +0 -151
151 lines not on this page · raw message
drivers/gpu/drm/renesas/rz-du/Kconfig new file +8 -0
@@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +config DRM_RZG2L_MIPI_DSI + tristate "RZ/G2L MIPI DSI Encoder Support" + depends on DRM && DRM_BRIDGE && OF + depends on ARCH_RENESAS || COMPILE_TEST + select DRM_MIPI_DSI + help + Enable support for the RZ/G2L Display Unit embedded MIPI DSI encoders.
drivers/gpu/drm/renesas/rz-du/Makefile new file +3 -0
@@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_DRM_RZG2L_MIPI_DSI) += rzg2l_mipi_dsi.o
drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c new file +816 -0
816 lines not on this page · raw message
drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi_regs.h new file +151 -0
151 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 217/261 drm: renesas: rzg2l_mipi_dsi: Increase reset deassertion delay
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Biju Das <[email protected]>
[ Upstream commit 7cbba8a8ba0219a267844d3116dbc77cecb4fcf8 ]
The RZ/G2L hardware manual (Rev. 1.50, May 2025), Section 34.4.2.1, requires waiting at least 1 msec after deasserting the CMN_RSTB signal before the DSI-Tx module is ready. Increase the delay from 1 usec to 1 msec by replacing udelay(1) with fsleep(1000) for RZ/G2L SoCs.
--- drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +1 -1
@@ -238,7 +238,7 @@ static int rzg2l_mipi_dsi_dphy_init(struif (ret <0 )return ret; -udelay (1 ); +fsleep (1000 );return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 218/261 usb: gadget: f_tcm: synchronize delayed set_alt with teardown
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Cen Zhang <[email protected]>
[ Upstream commit 79e2d75725c85607f8a9d87ae9cace62a19f767d ]
The f_tcm set_alt() path defers endpoint setup to a work item and completes the delayed status response from process context. The delayed work uses f_tcm private state and may complete the setup request after disconnect or function teardown has already moved on.
Cancel and drain the delayed set_alt work when the function is unbound or freed. For disable paths, which are reached under the composite device lock, use a small state machine and a non-sleeping cancellation path instead of cancel_work_sync(). If the work is already running, mark it cancelled and let the worker own the cleanup; otherwise tcm_disable() can cancel the queued work and clean up immediately.
Also serialize the final delayed-status completion with the cancellation check while holding the composite device lock. This prevents a disconnect from clearing delayed_status while the worker is about to complete the control request.
Validation reproduced this kernel report:
BUG: KASAN: slab-use-after-free in tcm_delayed_set_alt+0x6c/0xef0 Call Trace: <TASK> dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? tcm_delayed_set_alt+0x6c/0xef0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x188/0x320 ? tcm_delayed_set_alt+0x6c/0xef0 kasan_report+0xe0/0x110 ? tcm_delayed_set_alt+0x6c/0xef0 tcm_delayed_set_alt+0x6c/0xef0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? process_one_work+0x4cb/0xb90 ? rcu_is_watching+0x20/0x50 ? tcm_delayed_set_alt+0x9/0xef0 process_one_work+0x4d7/0xb90 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __list_add_valid_or_report+0x37/0xf0 ? __pfx_tcm_delayed_set_alt+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x2d8/0x570 ? __pfx_worker_thread+0x10/0x10 kthread+0x1ad/0x1f0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x3c9/0x540 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x2e9/0x730 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> Allocated by task 544: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0x8f/0xa0 tcm_alloc+0x68/0x180 usb_get_function+0x36/0x60 config_usb_cfg_link+0x125/0x1b0 configfs_symlink+0x322/0x890 vfs_symlink+0xc2/0x270 filename_symlinkat+0x295/0x2f0 __x64_sys_symlinkat+0x62/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 661: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x43/0x70 kfree+0x2f9/0x530 config_usb_cfg_unlink+0x173/0x1e0 configfs_unlink+0x1fa/0x340 vfs_unlink+0x15c/0x510 filename_unlinkat+0x2ba/0x450 __x64_sys_unlinkat+0x63/0x90 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f
--- drivers/usb/gadget/function/f_tcm.c | 192 ++++++++++++++++++++++++++++++------ drivers/usb/gadget/function/tcm.h | 13 ++ 2 files changed, 177 insertions(+), 28 deletions(-)
drivers/usb/gadget/function/f_tcm.c +164 -28
@@ -2011,31 +2011,158 @@ ep_fail:return -ENOTSUPP ; } -struct guas_setup_wq { -struct work_struct work; -struct f_uas *fu; -unsigned int alt; -}; +static void tcm_cleanup_old_alt (struct f_uas *fu) +{ +if (fu->flags &USBG_IS_UAS ) +uasp_cleanup_old_alt (fu); +else if (fu->flags &USBG_IS_BOT ) +bot_cleanup_old_alt (fu); + fu->flags =0 ; +} + +static void tcm_delayed_set_alt_done (struct f_uas *fu) +{ +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +} + +static bool tcm_delayed_set_alt_cancelled (struct f_uas *fu) +{ +bool cancelled; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + cancelled = fu->delayed_set_alt_cancel; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +return cancelled; +} + +static bool tcm_complete_delayed_status (struct f_uas *fu) +{ +struct usb_composite_dev *cdev = fu->function.config->cdev; +struct usb_request *req = cdev->req; +unsigned long cdev_flags; +bool cancelled; +int ret; + +spin_lock_irqsave (&cdev->lock, cdev_flags); +spin_lock (&fu->delayed_set_alt_lock); + cancelled = fu->delayed_set_alt_cancel; +if (!cancelled) { + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; + } +spin_unlock (&fu->delayed_set_alt_lock); + +if (cancelled) { +spin_unlock_irqrestore (&cdev->lock, cdev_flags); +return false; + } + +if (cdev->delayed_status ==0 ) { +WARN (cdev,"%s: Unexpected call\n" , __func__); + }else if (--cdev->delayed_status ==0 ) { + req->length =0 ; + req->context = cdev; + ret =usb_ep_queue (cdev->gadget->ep0, req,GFP_ATOMIC ); +if (ret ==0 ) { + cdev->setup_pending = true; + }else { + req->status =0 ; + req->complete (cdev->gadget->ep0, req); + } + } + +spin_unlock_irqrestore (&cdev->lock, cdev_flags); + +return true; +} + +static bool tcm_cancel_delayed_set_alt (struct f_uas *fu) +{ +bool cleanup = false; +bool cancel = false; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +switch (fu->delayed_set_alt_state) { +case USBG_DELAYED_SET_ALT_IDLE : + cleanup = true; +break ; +case USBG_DELAYED_SET_ALT_QUEUED : +case USBG_DELAYED_SET_ALT_RUNNING : + fu->delayed_set_alt_cancel = true; + cancel = true; +break ; + } +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +if (cancel &&cancel_work (&fu->delayed_set_alt)) { +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state ==USBG_DELAYED_SET_ALT_QUEUED ) { + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; + cleanup = true; + } +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + } + +return cleanup; +} + +static void tcm_cancel_delayed_set_alt_sync (struct f_uas *fu) +{ +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_IDLE ) + fu->delayed_set_alt_cancel = true; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); + +cancel_work_sync (&fu->delayed_set_alt); + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_IDLE ; + fu->delayed_set_alt_cancel = false; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +}static void tcm_delayed_set_alt (struct work_struct *wq) { - struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq, - work); -struct f_uas *fu = work->fu; -int alt = work->alt; + struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt); +unsigned long flags; +unsigned int alt; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_QUEUED ) { +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); +return ; + } + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_RUNNING ; + alt = fu->delayed_alt; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); -kfree (work); +tcm_cleanup_old_alt (fu); -if (fu->flags &USBG_IS_BOT ) -bot_cleanup_old_alt (fu); -if (fu->flags &USBG_IS_UAS ) -uasp_cleanup_old_alt (fu); +if (tcm_delayed_set_alt_cancelled (fu)) + goto out_done;if (alt ==USB_G_ALT_INT_BBB )bot_set_alt (fu);else if (alt ==USB_G_ALT_INT_UAS )uasp_set_alt (fu); -usb_composite_setup_continue (fu->function.config->cdev); + +if (tcm_complete_delayed_status (fu)) +return ; + +tcm_cleanup_old_alt (fu); +out_done: +tcm_delayed_set_alt_done (fu); }static int tcm_get_alt (struct usb_function *f,unsigned intf)
@@ -2061,15 +2188,20 @@ static int tcm_set_alt(struct usb_functireturn -EOPNOTSUPP ;if ((alt ==USB_G_ALT_INT_BBB ) || (alt ==USB_G_ALT_INT_UAS )) { -struct guas_setup_wq *work; +unsigned long flags; + +spin_lock_irqsave (&fu->delayed_set_alt_lock, flags); +if (fu->delayed_set_alt_state !=USBG_DELAYED_SET_ALT_IDLE ) { +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, + flags); +return -EBUSY ; + } + fu->delayed_alt = alt; + fu->delayed_set_alt_cancel = false; + fu->delayed_set_alt_state =USBG_DELAYED_SET_ALT_QUEUED ; +spin_unlock_irqrestore (&fu->delayed_set_alt_lock, flags); - work =kmalloc (sizeof (*work),GFP_ATOMIC ); -if (!work) -return -ENOMEM ; -INIT_WORK (&work->work, tcm_delayed_set_alt); - work->fu = fu; - work->alt = alt; -schedule_work (&work->work); +schedule_work (&fu->delayed_set_alt);return USB_GADGET_DELAYED_STATUS ; }return -EOPNOTSUPP ;
@@ -2079,11 +2211,8 @@ static void tcm_disable(struct usb_funct {struct f_uas *fu =to_f_uas (f); -if (fu->flags &USBG_IS_UAS ) -uasp_cleanup_old_alt (fu); -else if (fu->flags &USBG_IS_BOT ) -bot_cleanup_old_alt (fu); - fu->flags =0 ; +if (tcm_cancel_delayed_set_alt (fu)) +tcm_cleanup_old_alt (fu); } static int tcm_setup(struct usb_function *f,
@@ -2231,11 +2360,16 @@ static void tcm_free(struct usb_function {struct f_uas *tcm =to_f_uas (f); +tcm_cancel_delayed_set_alt_sync (tcm);kfree (tcm); }static void tcm_unbind (struct usb_configuration *c,struct usb_function *f) { +struct f_uas *fu =to_f_uas (f); + +tcm_cancel_delayed_set_alt_sync (fu); +tcm_cleanup_old_alt (fu);usb_free_all_descriptors (f); }
@@ -2268,6 +2402,8 @@ static struct usb_function *tcm_alloc(st fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; fu->tpg = tpg_instances[i].tpg; +INIT_WORK (&fu->delayed_set_alt, tcm_delayed_set_alt); +spin_lock_init (&fu->delayed_set_alt_lock);mutex_unlock (&tpg_instances_lock);return &fu->function;
drivers/usb/gadget/function/tcm.h +13 -0
@@ -3,6 +3,7 @@#define __TARGET_USB_GADGET_H__#include <linux/kref.h> +#include <linux/spinlock.h> /* #include <linux/usb/uas.h> */ #include <linux/usb/composite.h> #include <linux/usb/uas.h>
@@ -26,6 +27,12 @@ enum {#define USB_G_DEFAULT_SESSION_TAGS 128 +enum { +USBG_DELAYED_SET_ALT_IDLE =0 , +USBG_DELAYED_SET_ALT_QUEUED , +USBG_DELAYED_SET_ALT_RUNNING , +}; +struct tcm_usbg_nexus {struct se_session *tvn_se_sess; };
@@ -117,6 +124,12 @@ struct f_uas {#define USBG_IS_BOT (1 << 3)#define USBG_BOT_CMD_PEND (1 << 4) +struct work_struct delayed_set_alt; +spinlock_t delayed_set_alt_lock;/* protects delayed_set_alt_* */ +unsigned int delayed_alt; +unsigned int delayed_set_alt_state; +bool delayed_set_alt_cancel; +struct usbg_cdb cmd;struct usb_ep *ep_in;struct usb_ep *ep_out;
Greg Kroah-Hartman
maintainerPATCH 219/261 usb: typec: ucsi: Only enable supported notifications
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Diogo Ivo <[email protected]>
[ Upstream commit 27ffe4ff0b33b3dcc97fd448fd1e38d31ade575b ]
The UCSI specification defines some notifications to be optional for the PPM to support. From these only enable the ones the PPM informs us are actually supported.
Stable-dep-of: 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration")
--- drivers/usb/typec/ucsi/ucsi.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-)
drivers/usb/typec/ucsi/ucsi.c +23 -2
@@ -1406,6 +1406,27 @@ out_unlock:return ret; } +static u64 ucsi_get_supported_notifications (struct ucsi *ucsi) +{ +u8 features = ucsi->cap.features; +u64 ntfy =UCSI_ENABLE_NTFY_ALL ; + +if (!(features &UCSI_CAP_ALT_MODE_DETAILS )) + ntfy &= ~UCSI_ENABLE_NTFY_CAM_CHANGE ; + +if (!(features &UCSI_CAP_PDO_DETAILS )) + ntfy &= ~(UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE | +UCSI_ENABLE_NTFY_CAP_CHANGE ); + +if (!(features &UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS )) + ntfy &= ~UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE ; + +if (!(features &UCSI_CAP_PD_RESET )) + ntfy &= ~UCSI_ENABLE_NTFY_PD_RESET_COMPLETE ; + +return ntfy; +} +/** * ucsi_init - Initialize UCSI interface * @ucsi: UCSI to be initialized
@@ -1466,8 +1487,8 @@ static int ucsi_init(struct ucsi *ucsi) goto err_unregister; } -/* Enable all notifications */ - ntfy =UCSI_ENABLE_NTFY_ALL ; +/* Enable all supported notifications */ + ntfy =ucsi_get_supported_notifications (ucsi); command =UCSI_SET_NOTIFICATION_ENABLE | ntfy; ret =ucsi_send_command (ucsi, command,NULL ,0 );if (ret <0 )
Greg Kroah-Hartman
maintainerPATCH 220/261 usb: typec: ucsi: split connector lock classes
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Senozhatsky <[email protected]>
[ Upstream commit 8c22256bbafad3dc5fdbe9f684d045b67ff06a68 ]
Lockdep detects a possible recursive locking scenario during ucsi init:
[ 5.418616] ============================================
[ 5.418634] WARNING: possible recursive locking detected
[ 5.418706] --------------------------------------------
[ 5.418725] kworker/4:1/82 is trying to acquire lock:
[ 5.418759] ffff888119a34648 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi]
[ 5.418801]
but task is already holding lock:
[ 5.418835] ffff888119a34080 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi]
[ 5.418884]
other info that might help us debug this:
[ 5.418904] Possible unsafe locking scenario:[ 5.418937] CPU0
[ 5.418956] ----
[ 5.418991] lock(&con->lock);
[ 5.419013] lock(&con->lock);
[ 5.419033]
*** DEADLOCK ***[ 5.419387] Call Trace: [ 5.419406] <TASK> [ 5.419425] dump_stack_lvl+0x61/0xa0 [ 5.419448] print_deadlock_bug+0x4a6/0x650 [ 5.419483] __lock_acquire+0x62b6/0x7f50 [ 5.419507] lock_acquire+0x11b/0x390 [ 5.419654] __mutex_lock+0xbc/0xcd0 [ 5.419741] ucsi_init_work+0x1a78/0x2eb0 [ 5.419785] ? worker_thread+0xf53/0x2bc0 [ 5.419819] worker_thread+0xff4/0x2bc0 [ 5.419842] kthread+0x2a7/0x330 [ 5.419863] ? __pfx_worker_thread+0x10/0x10 [ 5.419896] ? __pfx_kthread+0x10/0x10 [ 5.419916] ret_from_fork+0x38/0x70 [ 5.419936] ? __pfx_kthread+0x10/0x10 [ 5.419969] ret_from_fork_asm+0x1b/0x30 [ 5.419991] </TASK> [ 5.420009] ---[ end trace 0000000000000000 ]---
The problem is that all connector locks belong to the same lockdep lock class, so the following loop:
for (i = 0; i < ucsi->cap.num_connectors; i++) ucsi_register_port(connector[i]) mutex_lock(&connector[i]->lock)
looks like a recursive acquire of the same mutex. Put each connector lock into a dedicated lock class so that lockdep doesn't see it as a possible recursion.
Stable-dep-of: 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration")
--- drivers/usb/typec/ucsi/ucsi.c | 8 ++++++++ drivers/usb/typec/ucsi/ucsi.h | 1 + 2 files changed, 9 insertions(+)
drivers/usb/typec/ucsi/ucsi.c +8 -0
@@ -1231,6 +1231,7 @@ static int ucsi_register_port(struct ucsINIT_WORK (&con->work, ucsi_handle_connector_change);init_completion (&con->complete);mutex_init (&con->lock); +lockdep_set_class (&con->lock, &con->lock_key);INIT_LIST_HEAD (&con->partner_tasks); con->ucsi = ucsi;
@@ -1479,6 +1480,9 @@ static int ucsi_init(struct ucsi *ucsi) goto err_reset; } +for (i =0 ; i < ucsi->cap.num_connectors; i++) +lockdep_register_key (&connector[i].lock_key); +/* Register all connectors */ for (i =0 ; i < ucsi->cap.num_connectors; i++) { connector[i].num = i +1 ;
@@ -1508,6 +1512,9 @@ static int ucsi_init(struct ucsi *ucsi)return 0 ; err_unregister: +for (i =0 ; i < ucsi->cap.num_connectors; i++) +lockdep_unregister_key (&connector[i].lock_key); +for (con = connector; con->port; con++) {if (con->wq)destroy_workqueue (con->wq);
@@ -1776,6 +1783,7 @@ void ucsi_unregister(struct ucsi *ucsi)usb_power_delivery_unregister (ucsi->connector[i].pd); ucsi->connector[i].pd =NULL ;typec_unregister_port (ucsi->connector[i].port); +lockdep_unregister_key (&ucsi->connector[i].lock_key); }kfree (ucsi->connector);
drivers/usb/typec/ucsi/ucsi.h +1 -0
@@ -350,6 +350,7 @@ struct ucsi_connector {struct ucsi *ucsi;struct mutex lock;/* port lock */ +struct lock_class_key lock_key;struct work_struct work;struct completion complete;struct workqueue_struct *wq;
Greg Kroah-Hartman
maintainerPATCH 221/261 usb: typec: ucsi: Fix race condition and ordering in port unregistration
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrei Kuchynski <[email protected]>
[ Upstream commit 7aa7d4bf9d3fa9a6a47b640ad103ab433b7ff261 ]
A synchronization issue exists during port unregistration where pending partner work items can race against workqueue destruction, leading to use-after-free conditions:
cros_ec_ucsi cros_ec_ucsi.3.auto: error -ETIMEDOUT: PPM init failed
BUG: kernel NULL pointer dereference, address: 0000000000000000
RIP: 0010:__queue_work+0x83/0x4a0
Call Trace:
<IRQ>
__cfi_delayed_work_timer_fn+0x10/0x10
run_timer_softirq+0x3b6/0xbd0
sched_clock_cpu+0xc/0x110
irq_exit_rcu+0x18d/0x330
fred_sysvec_apic_timer_interrupt+0x5e/0x80Fix this by ensuring strict ordering and proper serialization during teardown:
1. Move ucsi_unregister_partner() to the beginning of the teardown sequence and protect it under the connector mutex lock.
2. Ensure all pending partner tasks are explicitly flushed and finished before the workqueue is destroyed.
3. Switch from mod_delayed_work() to a cancel_delayed_work() and queue_delayed_work() sequence. This guarantees that items currently marked as pending won't be scheduled an additional time, preventing a double release of resources which leads to the following crash:
Oops: general protection fault, probably for non-canonical address
0xdead000000000122: 0000 [#1] SMP NOPTI
Workqueue: cros_ec_ucsi.3.auto-con2 ucsi_poll_worker
RIP: 0010:ucsi_poll_worker+0x65/0x1e0
Call Trace:
<TASK>
process_scheduled_works+0x218/0x6d0
worker_thread+0x188/0x3f0
__cfi_worker_thread+0x10/0x10
kthread+0x226/0x2a0To ensure these rules are applied identically across both the normal teardown and the ucsi_init() error paths, consolidate the cleanup logic into a new helper, ucsi_unregister_port().
--- drivers/usb/typec/ucsi/ucsi.c | 82 +++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 43 deletions(-)
drivers/usb/typec/ucsi/ucsi.c +39 -43
@@ -1407,6 +1407,42 @@ out_unlock:return ret; } +static void ucsi_unregister_port (struct ucsi_connector *con) +{ +struct ucsi_work *uwork; + +if (con->wq) { +mutex_lock (&con->lock); +ucsi_unregister_partner (con); +/* +* queue delayed items immediately so they can execute +* and free themselves before the wq is destroyed +*/ +list_for_each_entry (uwork, &con->partner_tasks, node) { +if (cancel_delayed_work (&uwork->work)) +queue_delayed_work (con->wq, &uwork->work,0 ); + } +mutex_unlock (&con->lock); + +destroy_workqueue (con->wq); + con->wq =NULL ; + }else { +ucsi_unregister_partner (con); + } + +ucsi_unregister_altmodes (con,UCSI_RECIPIENT_CON ); +ucsi_unregister_port_psy (con); + +usb_power_delivery_unregister_capabilities (con->port_sink_caps); + con->port_sink_caps =NULL ; +usb_power_delivery_unregister_capabilities (con->port_source_caps); + con->port_source_caps =NULL ; +usb_power_delivery_unregister (con->pd); + con->pd =NULL ; +typec_unregister_port (con->port); + con->port =NULL ; +} +static u64 ucsi_get_supported_notifications (struct ucsi *ucsi) {u8 features = ucsi->cap.features;
@@ -1515,22 +1551,8 @@ err_unregister:for (i =0 ; i < ucsi->cap.num_connectors; i++)lockdep_unregister_key (&connector[i].lock_key); -for (con = connector; con->port; con++) { -if (con->wq) -destroy_workqueue (con->wq); -ucsi_unregister_partner (con); -ucsi_unregister_altmodes (con,UCSI_RECIPIENT_CON ); -ucsi_unregister_port_psy (con); - -usb_power_delivery_unregister_capabilities (con->port_sink_caps); - con->port_sink_caps =NULL ; -usb_power_delivery_unregister_capabilities (con->port_source_caps); - con->port_source_caps =NULL ; -usb_power_delivery_unregister (con->pd); - con->pd =NULL ; -typec_unregister_port (con->port); - con->port =NULL ; - } +for (con = connector; con->port; con++) +ucsi_unregister_port (con);kfree (connector); err_reset:memset (&ucsi->cap,0 ,sizeof (ucsi->cap));
@@ -1756,33 +1778,7 @@ void ucsi_unregister(struct ucsi *ucsi)for (i =0 ; i < ucsi->cap.num_connectors; i++) {cancel_work_sync (&ucsi->connector[i].work); - -if (ucsi->connector[i].wq) { -struct ucsi_work *uwork; - -mutex_lock (&ucsi->connector[i].lock); -/* -* queue delayed items immediately so they can execute -* and free themselves before the wq is destroyed -*/ -list_for_each_entry (uwork, &ucsi->connector[i].partner_tasks, node) -mod_delayed_work (ucsi->connector[i].wq, &uwork->work,0 ); -mutex_unlock (&ucsi->connector[i].lock); -destroy_workqueue (ucsi->connector[i].wq); - } - -ucsi_unregister_partner (&ucsi->connector[i]); -ucsi_unregister_altmodes (&ucsi->connector[i], -UCSI_RECIPIENT_CON ); -ucsi_unregister_port_psy (&ucsi->connector[i]); - -usb_power_delivery_unregister_capabilities (ucsi->connector[i].port_sink_caps); - ucsi->connector[i].port_sink_caps =NULL ; -usb_power_delivery_unregister_capabilities (ucsi->connector[i].port_source_caps); - ucsi->connector[i].port_source_caps =NULL ; -usb_power_delivery_unregister (ucsi->connector[i].pd); - ucsi->connector[i].pd =NULL ; -typec_unregister_port (ucsi->connector[i].port); +ucsi_unregister_port (&ucsi->connector[i]);lockdep_unregister_key (&ucsi->connector[i].lock_key); }
Greg Kroah-Hartman
maintainerPATCH 222/261 drm/fb-helper: Allocate and release fb_info in single place
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit 63c971af40365ee706c7e24f6a7900d693518f09 ]
Move the calls to drm_fb_helper_alloc_info() from drivers into a single place in fbdev helpers. Allocates struct fb_info for a new framebuffer device. Then call drm_fb_helper_single_fb_probe() to create an fbdev screen buffer. Also release the instance on errors by calling drm_fb_helper_release_info().
Simplifies the code and fixes the error cleanup for some of the drivers.
Regular release of the struct fb_info instance still happens in drm_fb_helper_fini() as before.
v2:
- remove error rollback in driver implementations (kernel test robot)
- initialize info in TTM implementation (kernel test robot)
Stable-dep-of: a18b6e30ecd6 ("drm/tegra: fbdev: Remove offset into framebuffer memory")
--- drivers/gpu/drm/armada/armada_fbdev.c | 12 -------- drivers/gpu/drm/drm_fb_helper.c | 41 +++++++++-------------------- drivers/gpu/drm/drm_fbdev_dma.c | 12 -------- drivers/gpu/drm/drm_fbdev_generic.c | 12 +------- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 9 ------ drivers/gpu/drm/gma500/fbdev.c | 13 --------- drivers/gpu/drm/i915/display/intel_fbdev.c | 9 ------ drivers/gpu/drm/msm/msm_fbdev.c | 9 ------ drivers/gpu/drm/omapdrm/omap_fbdev.c | 9 ------ drivers/gpu/drm/radeon/radeon_fbdev.c | 13 --------- drivers/gpu/drm/tegra/fbdev.c | 9 ------ include/drm/drm_fb_helper.h | 12 -------- 12 files changed, 25 insertions(+), 135 deletions(-)
drivers/gpu/drm/armada/armada_fbdev.c +1 -11
@@ -43,10 +43,10 @@ static int armada_fbdev_create(struct dr struct drm_fb_helper_surface_size *sizes) {struct drm_device *dev = fbh->dev; +struct fb_info *info = fbh->info;struct drm_mode_fb_cmd2 mode;struct armada_framebuffer *dfb;struct armada_gem_object *obj; -struct fb_info *info;int size, ret;void *ptr;
@@ -87,12 +87,6 @@ static int armada_fbdev_create(struct drif (IS_ERR (dfb))return PTR_ERR (dfb); - info =drm_fb_helper_alloc_info (fbh); -if (IS_ERR (info)) { - ret =PTR_ERR (info); - goto err_fballoc; - } - info->fbops = &armada_fb_ops; info->fix.smem_start = obj->phys_addr; info->fix.smem_len = obj->obj.size;
@@ -107,10 +101,6 @@ static int armada_fbdev_create(struct dr (unsigned long long)obj->phys_addr);return 0 ; - - err_fballoc: - dfb->fb.funcs->destroy (&dfb->fb); -return ret; } static int armada_fb_probe(struct drm_fb_helper *fbh,
drivers/gpu/drm/drm_fb_helper.c +14 -27
83 lines not on this page · raw message
drivers/gpu/drm/drm_fbdev_dma.c +1 -11
@@ -78,10 +78,10 @@ static int drm_fbdev_dma_helper_fb_probe {struct drm_client_dev *client = &fb_helper->client;struct drm_device *dev = fb_helper->dev; +struct fb_info *info = fb_helper->info;struct drm_client_buffer *buffer;struct drm_gem_dma_object *dma_obj;struct drm_framebuffer *fb; -struct fb_info *info;u32 format;struct iosys_map map;int ret;
@@ -114,12 +114,6 @@ static int drm_fbdev_dma_helper_fb_probe fb_helper->buffer = buffer; fb_helper->fb = buffer->fb; - info =drm_fb_helper_alloc_info (fb_helper); -if (IS_ERR (info)) { - ret =PTR_ERR (info); - goto err_drm_client_buffer_vunmap; - } -drm_fb_helper_fill_info (info, fb_helper, sizes); info->fbops = &drm_fbdev_dma_fb_ops;
@@ -138,10 +132,6 @@ static int drm_fbdev_dma_helper_fb_probereturn 0 ; -err_drm_client_buffer_vunmap: - fb_helper->fb =NULL ; - fb_helper->buffer =NULL ; -drm_client_buffer_vunmap (buffer); err_drm_client_buffer_delete:drm_client_framebuffer_delete (buffer);return ret;
drivers/gpu/drm/drm_fbdev_generic.c +2 -10
@@ -73,8 +73,8 @@ static int drm_fbdev_generic_helper_fb_p {struct drm_client_dev *client = &fb_helper->client;struct drm_device *dev = fb_helper->dev; +struct fb_info *info = fb_helper->info;struct drm_client_buffer *buffer; -struct fb_info *info;size_t screen_size;void *screen_buffer;u32 format;
@@ -100,12 +100,6 @@ static int drm_fbdev_generic_helper_fb_p goto err_drm_client_framebuffer_delete; } - info =drm_fb_helper_alloc_info (fb_helper); -if (IS_ERR (info)) { - ret =PTR_ERR (info); - goto err_vfree; - } -drm_fb_helper_fill_info (info, fb_helper, sizes); info->fbops = &drm_fbdev_generic_fb_ops;
@@ -122,12 +116,10 @@ static int drm_fbdev_generic_helper_fb_p info->fbdefio = &fb_helper->fbdefio; ret =fb_deferred_io_init (info);if (ret) - goto err_drm_fb_helper_release_info; + goto err_vfree;return 0 ; -err_drm_fb_helper_release_info: -drm_fb_helper_release_info (fb_helper); err_vfree:vfree (screen_buffer); err_drm_client_framebuffer_delete:
drivers/gpu/drm/exynos/exynos_drm_fbdev.c +1 -8
@@ -60,18 +60,11 @@ static int exynos_drm_fbdev_update(strucstruct drm_fb_helper_surface_size *sizes, struct exynos_drm_gem *exynos_gem) { - struct fb_info *fbi; + struct fb_info *fbi = helper->info;struct drm_framebuffer *fb = helper->fb;unsigned int size = fb->width * fb->height * fb->format->cpp[0 ];unsigned long offset; - fbi =drm_fb_helper_alloc_info (helper); -if (IS_ERR (fbi)) { -DRM_DEV_ERROR (to_dma_dev (helper->dev), -"failed to allocate fb info.\n" ); -return PTR_ERR (fbi); - } - fbi->fbops = &exynos_drm_fb_ops;drm_fb_helper_fill_info (fbi, helper, sizes);
drivers/gpu/drm/gma500/fbdev.c +1 -12
@@ -110,7 +110,7 @@ static int psb_fbdev_fb_probe(struct drmstruct drm_device *dev = fb_helper->dev;struct drm_psb_private *dev_priv =to_drm_psb_private (dev);struct pci_dev *pdev =to_pci_dev (dev->dev); -struct fb_info *info; +struct fb_info *info = fb_helper->info;struct drm_framebuffer *fb;struct drm_mode_fb_cmd2 mode_cmd = { };int size;
@@ -165,12 +165,6 @@ static int psb_fbdev_fb_probe(struct drm fb_helper->fb = fb; - info =drm_fb_helper_alloc_info (fb_helper); -if (IS_ERR (info)) { - ret =PTR_ERR (info); - goto err_drm_framebuffer_unregister_private; - } - info->fbops = &psb_fbdev_fb_ops;/* Accessed stolen memory directly */
@@ -194,11 +188,6 @@ static int psb_fbdev_fb_probe(struct drmreturn 0 ; -err_drm_framebuffer_unregister_private: -drm_framebuffer_unregister_private (fb); - fb->obj[0 ] =NULL ; -drm_framebuffer_cleanup (fb); -kfree (fb); err_drm_gem_object_put:drm_gem_object_put (obj);return ret;
drivers/gpu/drm/i915/display/intel_fbdev.c +1 -8
@@ -218,7 +218,7 @@ static int intelfb_create(struct drm_fb_ .type = I915_GTT_VIEW_NORMAL, };intel_wakeref_t wakeref; -struct fb_info *info; +struct fb_info *info = helper->info;struct i915_vma *vma;unsigned long flags =0 ;bool prealloc = false;
@@ -271,13 +271,6 @@ static int intelfb_create(struct drm_fb_ goto out_unlock; } - info =drm_fb_helper_alloc_info (helper); -if (IS_ERR (info)) { -drm_err (&dev_priv->drm,"Failed to allocate fb_info (%pe)\n" , info); - ret =PTR_ERR (info); - goto out_unpin; - } - ifbdev->helper.fb = &ifbdev->fb->base; info->fbops = &intelfb_ops;
drivers/gpu/drm/msm/msm_fbdev.c +1 -8
@@ -70,9 +70,9 @@ static int msm_fbdev_create(struct drm_f {struct drm_device *dev = helper->dev;struct msm_drm_private *priv = dev->dev_private; +struct fb_info *fbi = helper->info;struct drm_framebuffer *fb =NULL ;struct drm_gem_object *bo; -struct fb_info *fbi =NULL ;uint64_t paddr;uint32_t format;int ret, pitch;
@@ -105,13 +105,6 @@ static int msm_fbdev_create(struct drm_f goto fail; } - fbi =drm_fb_helper_alloc_info (helper); -if (IS_ERR (fbi)) { -DRM_DEV_ERROR (dev->dev,"failed to allocate fb info\n" ); - ret =PTR_ERR (fbi); - goto fail; - } -DBG ("fbi=%p, dev=%p" , fbi, dev); helper->fb = fb;
drivers/gpu/drm/omapdrm/omap_fbdev.c +1 -8
@@ -131,9 +131,9 @@ static int omap_fbdev_create(struct drm_struct omap_fbdev *fbdev =to_omap_fbdev (helper);struct drm_device *dev = helper->dev;struct omap_drm_private *priv = dev->dev_private; +struct fb_info *fbi = helper->info;struct drm_framebuffer *fb =NULL ;union omap_gem_size gsize; -struct fb_info *fbi =NULL ;struct drm_mode_fb_cmd2 mode_cmd = {0 };struct drm_gem_object *bo;dma_addr_t dma_addr;
@@ -199,13 +199,6 @@ static int omap_fbdev_create(struct drm_ goto fail; } - fbi =drm_fb_helper_alloc_info (helper); -if (IS_ERR (fbi)) { -dev_err (dev->dev,"failed to allocate fb info\n" ); - ret =PTR_ERR (fbi); - goto fail; - } -DBG ("fbi=%p, dev=%p" , fbi, dev); helper->fb = fb;
drivers/gpu/drm/radeon/radeon_fbdev.c +1 -12
@@ -207,7 +207,7 @@ static int radeon_fbdev_fb_helper_fb_pro {struct radeon_device *rdev = fb_helper->dev->dev_private;struct drm_mode_fb_cmd2 mode_cmd = { }; -struct fb_info *info; +struct fb_info *info = fb_helper->info;struct drm_gem_object *gobj;struct radeon_bo *rbo;struct drm_framebuffer *fb;
@@ -245,13 +245,6 @@ static int radeon_fbdev_fb_helper_fb_pro/* setup helper */ fb_helper->fb = fb; -/* okay we have an object now allocate the framebuffer */ - info =drm_fb_helper_alloc_info (fb_helper); -if (IS_ERR (info)) { - ret =PTR_ERR (info); - goto err_drm_framebuffer_unregister_private; - } - info->fbops = &radeon_fbdev_fb_ops;/* radeon resume is fragile and needs a vt switch to help it along */
@@ -277,10 +270,6 @@ static int radeon_fbdev_fb_helper_fb_proreturn 0 ; -err_drm_framebuffer_unregister_private: - fb_helper->fb =NULL ; -drm_framebuffer_unregister_private (fb); -drm_framebuffer_cleanup (fb); err_kfree:kfree (fb); err_radeon_fbdev_destroy_pinned_object:
drivers/gpu/drm/tegra/fbdev.c +1 -8
@@ -72,10 +72,10 @@ static int tegra_fbdev_probe(struct drm_struct tegra_drm *tegra = helper->dev->dev_private;struct drm_device *drm = helper->dev;struct drm_mode_fb_cmd2 cmd = {0 }; +struct fb_info *info = helper->info;unsigned int bytes_per_pixel;struct drm_framebuffer *fb;unsigned long offset; -struct fb_info *info;struct tegra_bo *bo;size_t size;int err;
@@ -96,13 +96,6 @@ static int tegra_fbdev_probe(struct drm_if (IS_ERR (bo))return PTR_ERR (bo); - info =drm_fb_helper_alloc_info (helper); -if (IS_ERR (info)) { -dev_err (drm->dev,"failed to allocate framebuffer info\n" ); -drm_gem_object_put (&bo->gem); -return PTR_ERR (info); - } - fb =tegra_fb_alloc (drm, &cmd, &bo,1 );if (IS_ERR (fb)) { err =PTR_ERR (fb);
include/drm/drm_fb_helper.h +0 -12
@@ -246,8 +246,6 @@ int drm_fb_helper_check_var(struct fb_vaint drm_fb_helper_restore_fbdev_mode_unlocked (struct drm_fb_helper *fb_helper); -struct fb_info *drm_fb_helper_alloc_info (struct drm_fb_helper *fb_helper); -void drm_fb_helper_release_info (struct drm_fb_helper *fb_helper);void drm_fb_helper_unregister_info (struct drm_fb_helper *fb_helper); void drm_fb_helper_fill_info(struct fb_info *info, struct drm_fb_helper *fb_helper,
@@ -330,16 +328,6 @@ drm_fb_helper_restore_fbdev_mode_unlockereturn 0 ; } -static inline struct fb_info * -drm_fb_helper_alloc_info (struct drm_fb_helper *fb_helper) -{ -return NULL ; -} - -static inline void drm_fb_helper_release_info (struct drm_fb_helper *fb_helper) -{ -} -static inline void drm_fb_helper_unregister_info (struct drm_fb_helper *fb_helper) { }
Greg Kroah-Hartman
maintainerPATCH 223/261 drm/tegra: fbdev: Remove offset into framebuffer memory
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
[ Upstream commit a18b6e30ecd69096beda4a0c96d2570900c3879a ]
The screen_buffer field in struct fb_info contains the kernel address of the first byte of framebuffer memory. Do not add the display offset. This offset only describes scrolling during scanout.
--- drivers/gpu/drm/tegra/fbdev.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
drivers/gpu/drm/tegra/fbdev.c +2 -6
@@ -75,7 +75,6 @@ static int tegra_fbdev_probe(struct drm_struct fb_info *info = helper->info;unsigned int bytes_per_pixel;struct drm_framebuffer *fb; -unsigned long offset;struct tegra_bo *bo;size_t size;int err;
@@ -112,9 +111,6 @@ static int tegra_fbdev_probe(struct drm_drm_fb_helper_fill_info (info, helper, sizes); - offset = info->var.xoffset * bytes_per_pixel + - info->var.yoffset * fb->pitches[0 ]; -if (bo->pages) { bo->vaddr =vmap (bo->pages, bo->num_pages,VM_MAP ,pgprot_writecombine (PAGE_KERNEL ));
@@ -126,9 +122,9 @@ static int tegra_fbdev_probe(struct drm_ } info->flags |=FBINFO_VIRTFB ; - info->screen_buffer = bo->vaddr + offset; + info->screen_buffer = bo->vaddr; info->screen_size = size; - info->fix.smem_start = (unsigned long )(bo->iova + offset); + info->fix.smem_start = (unsigned long )(bo->iova); info->fix.smem_len = size;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 224/261 drm/amdgpu: Respect placement requirements in amdgpu_gtt_mgr functions
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
[ Upstream commit 8882f8897e554053af9e72f4c2da8b1e2cce56c7 ]
When testing intersection and compatibility, respect the actual placement requirements. This is a pre-requisite for ensuring that UVD CS BOs do not cross 256M segments.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c | 30 ++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +28 -2
@@ -219,7 +219,20 @@ static bool amdgpu_gtt_mgr_intersects(st const struct ttm_place *place, size_t size) { -return !place->lpfn ||amdgpu_gtt_mgr_has_gart_addr (res); +const struct drm_mm_node *const node = &to_ttm_range_mgr_node (res)->mm_nodes[0 ]; +const u32 num_pages =PFN_UP (size); + +if (!place->lpfn) +return true; + +if (!amdgpu_gtt_mgr_has_gart_addr (res)) +return false; + +if (place->fpfn >= (node->start + num_pages) || + (place->lpfn && place->lpfn <= node->start)) +return false; + +return true; }/**
@@ -237,7 +250,20 @@ static bool amdgpu_gtt_mgr_compatible(st const struct ttm_place *place, size_t size) { -return !place->lpfn ||amdgpu_gtt_mgr_has_gart_addr (res); +const struct drm_mm_node *const node = &to_ttm_range_mgr_node (res)->mm_nodes[0 ]; +const u32 num_pages =PFN_UP (size); + +if (!place->lpfn) +return true; + +if (!amdgpu_gtt_mgr_has_gart_addr (res)) +return false; + +if (node->start < place->fpfn || + (place->lpfn && (node->start + num_pages) > place->lpfn)) +return false; + +return true; }/**
Greg Kroah-Hartman
maintainerPATCH 225/261 drm/i915/vrr: Check HAS_VRR() first in intel_vrr_is_capable()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ville Syrjälä <[email protected]>
[ Upstream commit 4b274b0b61ab2a529e5c22e9aa033f3028e639fc ]
There's no point in doing all the other checks in intel_vrr_is_capable() if the platform doesn't support VRR at all Check HAS_VRR() before wasting time on the other checks.
Stable-dep-of: f8a9262c7a6f ("drm/i915/vrr: require valid min/max vfreq for VRR")
--- drivers/gpu/drm/i915/display/intel_vrr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/gpu/drm/i915/display/intel_vrr.c +4 -2
@@ -16,6 +16,9 @@ bool intel_vrr_is_capable(struct intel_cstruct drm_i915_private *i915 =to_i915 (connector->base.dev);struct intel_dp *intel_dp; +if (!HAS_VRR (i915)) +return false; +/* * DP Sink is capable of VRR video timings if * Ignore MSA bit is set in DPCD.
@@ -38,8 +41,7 @@ bool intel_vrr_is_capable(struct intel_creturn false; } -return HAS_VRR (i915) && - info->monitor_range.max_vfreq - info->monitor_range.min_vfreq >10 ; +return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq >10 ; }void
Greg Kroah-Hartman
maintainerPATCH 226/261 drm/i915/vrr: require valid min/max vfreq for VRR
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit f8a9262c7a6fc2de9802e14b0228114f0333869e ]
Ensure the EDID provided min/max vfreq are valid. Most scenarios are already covered (by coincidence) through the checks in intel_vrr_is_capable() and intel_vrr_is_in_range(), but be more explicit about it. At worst, a zero min_vfreq could lead to a division by zero in intel_vrr_compute_vmax().
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/display/intel_vrr.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/gpu/drm/i915/display/intel_vrr.c +4 -0
@@ -41,6 +41,10 @@ bool intel_vrr_is_capable(struct intel_creturn false; } +if (!info->monitor_range.min_vfreq || !info->monitor_range.max_vfreq || + info->monitor_range.min_vfreq > info->monitor_range.max_vfreq) +return false; +return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq >10 ; }
Greg Kroah-Hartman
maintainerPATCH 227/261 media: i2c: imx219: Access height from active format in imx219_set_ctrl
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laurent Pinchart <[email protected]>
[ Upstream commit aa86ac42eec4dd7e987c12390a0a487187e2d9ae ]
Use the active format height instead of the mode height in imx219_set_ctrl(). This prepares for dropping the mode field from the imx219 structure.
The state is retrieved using v4l2_subdev_get_locked_active_state() as the subdev active state and the control handler share the same lock.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/media/i2c/imx219.c +7 -2
@@ -500,13 +500,18 @@ static int imx219_set_ctrl(struct v4l2_cstruct imx219 *imx219 = container_of(ctrl->handler, struct imx219, ctrl_handler);struct i2c_client *client =v4l2_get_subdevdata (&imx219->sd); +const struct v4l2_mbus_framefmt *format; +struct v4l2_subdev_state *state;int ret =0 ; + state =v4l2_subdev_get_locked_active_state (&imx219->sd); + format =v4l2_subdev_get_pad_format (&imx219->sd, state,0 ); +if (ctrl->id ==V4L2_CID_VBLANK ) {int exposure_max, exposure_def;/* Update max exposure while meeting expected vblanking */ - exposure_max = imx219->mode->height + ctrl->val -4 ; + exposure_max = format->height + ctrl->val -4 ; exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? exposure_max :IMX219_EXPOSURE_DEFAULT ; __v4l2_ctrl_modify_range(imx219->exposure,
@@ -546,7 +551,7 @@ static int imx219_set_ctrl(struct v4l2_cbreak ;case V4L2_CID_VBLANK :cci_write (imx219->regmap,IMX219_REG_VTS , - imx219->mode->height + ctrl->val, &ret); + format->height + ctrl->val, &ret);break ;case V4L2_CID_TEST_PATTERN_RED : cci_write(imx219->regmap, IMX219_REG_TESTP_RED,
Greg Kroah-Hartman
maintainerPATCH 228/261 media: i2c: imx219: Dont store the current mode in the imx219 structure
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laurent Pinchart <[email protected]>
[ Upstream commit e3e5d172d5fce9151bc101427554a158d4759856 ]
The mode field of the imx219 structure is only used in imx219_init_controls(), after the probe function sets it to point to the default mode. Use the default mode directly when initializing controls, and drop the mode field from the imx219 structure.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
drivers/media/i2c/imx219.c +7 -6
@@ -1067,8 +1067,8 @@ static unsigned long imx219_get_pixel_rastatic int imx219_init_controls (struct imx219 *imx219) {struct i2c_client *client =v4l2_get_subdevdata (&imx219->sd); +const struct imx219_mode *mode = &supported_modes[0 ];struct v4l2_ctrl_handler *ctrl_hdlr; -unsigned int height = imx219->mode->height;struct v4l2_fwnode_device_properties props;int exposure_max, exposure_def, hblank;int i, ret;
@@ -1097,15 +1097,15 @@ static int imx219_init_controls(struct i/* Initial vblank/hblank/exposure parameters based on current mode */ imx219->vblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_VBLANK ,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - height,1 , - imx219->mode->vts_def - height); - hblank =IMX219_PPL_DEFAULT - imx219->mode->width; +IMX219_VTS_MAX - mode->height,1 , + mode->vts_def - mode->height); + hblank =IMX219_PPL_DEFAULT - mode->width; imx219->hblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_HBLANK , hblank, hblank,1 , hblank);if (imx219->hblank) imx219->hblank->flags |=V4L2_CTRL_FLAG_READ_ONLY ; - exposure_max = imx219->mode->vts_def -4 ; + exposure_max = mode->vts_def -4 ; exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? exposure_max :IMX219_EXPOSURE_DEFAULT ; imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
@@ -1298,7 +1298,8 @@ static int imx219_probe(struct i2c_clien/* Set default mode to max resolution */ imx219->mode = &supported_modes[0 ]; -/* sensor doesn't enter LP-11 state upon power up until and unless +/* +* Sensor doesn't enter LP-11 state upon power up until and unless * streaming is started, so upon power up switch the modes to: * streaming -> standby */
Greg Kroah-Hartman
maintainerPATCH 229/261 media: i2c: imx219: Drop IMX219_VTS_* macros
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laurent Pinchart <[email protected]>
[ Upstream commit 5ebbdd7aab3321e60a8be23aac1fee4f16644021 ]
The IMX219_VTS_* macros define default VTS values for the modes supported by the driver. They are used in a single place, and hinder readability compared to using the value directly as a decimal number. Drop them.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)
drivers/media/i2c/imx219.c +4 -14
@@ -71,20 +71,10 @@/* V_TIMING internal */ #define IMX219_REG_VTS CCI_REG16(0x0160) -#define IMX219_VTS_15FPS 0x0dc6 -#define IMX219_VTS_30FPS_1080P 0x06e3 -#define IMX219_VTS_30FPS_BINNED 0x06e3 -#define IMX219_VTS_30FPS_640x480 0x06e3#define IMX219_VTS_MAX 0xffff#define IMX219_VBLANK_MIN 32 -/*Frame Length Line*/ -#define IMX219_FLL_MIN 0x08a6 -#define IMX219_FLL_MAX 0xffff -#define IMX219_FLL_STEP 1 -#define IMX219_FLL_DEFAULT 0x0c98 -/* HBLANK control - read only */ #define IMX219_PPL_DEFAULT 3448
@@ -381,7 +371,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = IMX219_VTS_15FPS, + .vts_def = 3526, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs,
@@ -398,7 +388,7 @@ static const struct imx219_mode supporte .width = 1920, .height =1080 }, - .vts_def = IMX219_VTS_30FPS_1080P, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), .regs = mode_1920_1080_regs,
@@ -415,7 +405,7 @@ static const struct imx219_mode supporte .width = 3280, .height =2464 }, - .vts_def = IMX219_VTS_30FPS_BINNED, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), .regs = mode_1640_1232_regs,
@@ -432,7 +422,7 @@ static const struct imx219_mode supporte .width = 1280, .height =960 }, - .vts_def = IMX219_VTS_30FPS_640x480, + .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), .regs = mode_640_480_regs,
Greg Kroah-Hartman
maintainerPATCH 230/261 media: i2c: imx219: Group functions by purpose
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laurent Pinchart <[email protected]>
[ Upstream commit d03dfb7d4c5fae0d3f297536063e00ea2c1129d5 ]
Move functions around to group them by purpose, in order to improve readability. No functional change is intended.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 580 +++++++++++++++++++++++---------------------- 1 file changed, 298 insertions(+), 282 deletions(-)
drivers/media/i2c/imx219.c +298 -282
@@ -485,6 +485,10 @@ static u32 imx219_get_format_code(structreturn imx219_mbus_formats[i]; } +/* ----------------------------------------------------------------------------- +* Controls +*/ +static int imx219_set_ctrl (struct v4l2_ctrl *ctrl) { struct imx219 *imx219 =
@@ -576,130 +580,135 @@ static const struct v4l2_ctrl_ops imx219 .s_ctrl = imx219_set_ctrl, }; -static void imx219_update_pad_format (struct imx219 *imx219, -const struct imx219_mode *mode, -struct v4l2_mbus_framefmt *fmt,u32 code) +static unsigned long imx219_get_pixel_rate (struct imx219 *imx219) { -/* Bayer order varies with flips */ - fmt->code =imx219_get_format_code (imx219, code); - fmt->width = mode->width; - fmt->height = mode->height; - fmt->field =V4L2_FIELD_NONE ; - fmt->colorspace =V4L2_COLORSPACE_RAW ; - fmt->quantization =V4L2_QUANTIZATION_FULL_RANGE ; - fmt->xfer_func =V4L2_XFER_FUNC_NONE ; +return (imx219->lanes ==2 ) ?IMX219_PIXEL_RATE :IMX219_PIXEL_RATE_4LANE ; } -static int imx219_init_cfg (struct v4l2_subdev *sd, -struct v4l2_subdev_state *state) +/* Initialize control handlers */ +static int imx219_init_controls (struct imx219 *imx219) { -struct imx219 *imx219 =to_imx219 (sd); -struct v4l2_mbus_framefmt *format; -struct v4l2_rect *crop; - -/* Initialize the format. */ - format =v4l2_subdev_get_pad_format (sd, state,0 ); -imx219_update_pad_format (imx219, &supported_modes[0 ], format, -MEDIA_BUS_FMT_SRGGB10_1X10 ); - -/* Initialize the crop rectangle. */ - crop =v4l2_subdev_get_pad_crop (sd, state,0 ); - crop->top =IMX219_PIXEL_ARRAY_TOP ; - crop->left =IMX219_PIXEL_ARRAY_LEFT ; - crop->width =IMX219_PIXEL_ARRAY_WIDTH ; - crop->height =IMX219_PIXEL_ARRAY_HEIGHT ; +struct i2c_client *client =v4l2_get_subdevdata (&imx219->sd); +const struct imx219_mode *mode = &supported_modes[0 ]; +struct v4l2_ctrl_handler *ctrl_hdlr; +struct v4l2_fwnode_device_properties props; +int exposure_max, exposure_def, hblank; +int i, ret; -return 0 ; -} + ctrl_hdlr = &imx219->ctrl_handler; + ret =v4l2_ctrl_handler_init (ctrl_hdlr,12 ); +if (ret) +return ret; -static int imx219_enum_mbus_code (struct v4l2_subdev *sd, -struct v4l2_subdev_state *sd_state, -struct v4l2_subdev_mbus_code_enum *code) -{ -struct imx219 *imx219 =to_imx219 (sd); +/* By default, PIXEL_RATE is read only */ + imx219->pixel_rate =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_PIXEL_RATE , +imx219_get_pixel_rate (imx219), +imx219_get_pixel_rate (imx219),1 , +imx219_get_pixel_rate (imx219)); -if (code->index >= (ARRAY_SIZE (imx219_mbus_formats) /4 )) -return -EINVAL ; + imx219->link_freq = +v4l2_ctrl_new_int_menu (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_LINK_FREQ , +ARRAY_SIZE (imx219_link_freq_menu) -1 ,0 , + (imx219->lanes ==2 ) ? imx219_link_freq_menu : + imx219_link_freq_4lane_menu); +if (imx219->link_freq) + imx219->link_freq->flags |=V4L2_CTRL_FLAG_READ_ONLY ; - code->code =imx219_get_format_code (imx219, imx219_mbus_formats[code->index *4 ]); +/* Initial vblank/hblank/exposure parameters based on current mode */ + imx219->vblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_VBLANK ,IMX219_VBLANK_MIN , +IMX219_VTS_MAX - mode->height,1 , + mode->vts_def - mode->height); + hblank =IMX219_PPL_DEFAULT - mode->width; + imx219->hblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_HBLANK , hblank, hblank, +1 , hblank); +if (imx219->hblank) + imx219->hblank->flags |=V4L2_CTRL_FLAG_READ_ONLY ; + exposure_max = mode->vts_def -4 ; + exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? + exposure_max :IMX219_EXPOSURE_DEFAULT ; + imx219->exposure =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_EXPOSURE , +IMX219_EXPOSURE_MIN , exposure_max, +IMX219_EXPOSURE_STEP , + exposure_def); -return 0 ; -} +v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_ANALOGUE_GAIN , +IMX219_ANA_GAIN_MIN ,IMX219_ANA_GAIN_MAX , +IMX219_ANA_GAIN_STEP ,IMX219_ANA_GAIN_DEFAULT ); -static int imx219_enum_frame_size (struct v4l2_subdev *sd, -struct v4l2_subdev_state *sd_state, -struct v4l2_subdev_frame_size_enum *fse) -{ -struct imx219 *imx219 =to_imx219 (sd); -u32 code; +v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_DIGITAL_GAIN , +IMX219_DGTL_GAIN_MIN ,IMX219_DGTL_GAIN_MAX , +IMX219_DGTL_GAIN_STEP ,IMX219_DGTL_GAIN_DEFAULT ); -if (fse->index >=ARRAY_SIZE (supported_modes)) -return -EINVAL ; + imx219->hflip =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_HFLIP ,0 ,1 ,1 ,0 ); +if (imx219->hflip) + imx219->hflip->flags |=V4L2_CTRL_FLAG_MODIFY_LAYOUT ; - code =imx219_get_format_code (imx219, fse->code); -if (fse->code != code) -return -EINVAL ; + imx219->vflip =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_VFLIP ,0 ,1 ,1 ,0 ); +if (imx219->vflip) + imx219->vflip->flags |=V4L2_CTRL_FLAG_MODIFY_LAYOUT ; - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; +v4l2_ctrl_new_std_menu_items (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_TEST_PATTERN , +ARRAY_SIZE (imx219_test_pattern_menu) -1 , +0 ,0 , imx219_test_pattern_menu); +for (i =0 ; i <4 ; i++) { +/* +* The assumption is that +* V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 +* V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 +* V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 +*/ +v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, +V4L2_CID_TEST_PATTERN_RED + i, +IMX219_TESTP_COLOUR_MIN , +IMX219_TESTP_COLOUR_MAX , +IMX219_TESTP_COLOUR_STEP , +IMX219_TESTP_COLOUR_MAX ); +/* The "Solid color" pattern is white by default */ + } -return 0 ; -} +if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; +dev_err (&client->dev,"%s control init failed (%d)\n" , + __func__, ret); + goto error; + } -static int imx219_set_pad_format (struct v4l2_subdev *sd, -struct v4l2_subdev_state *sd_state, -struct v4l2_subdev_format *fmt) -{ -struct imx219 *imx219 =to_imx219 (sd); -const struct imx219_mode *mode; -int exposure_max, exposure_def, hblank; -struct v4l2_mbus_framefmt *format; -struct v4l2_rect *crop; + ret =v4l2_fwnode_device_parse (&client->dev, &props); +if (ret) + goto error; - mode =v4l2_find_nearest_size (supported_modes, -ARRAY_SIZE (supported_modes), - width, height, - fmt->format.width, fmt->format.height); + ret =v4l2_ctrl_new_fwnode_properties (ctrl_hdlr, &imx219_ctrl_ops, + &props); +if (ret) + goto error; -imx219_update_pad_format (imx219, mode, &fmt->format, fmt->format.code); + imx219->sd.ctrl_handler = ctrl_hdlr; - format =v4l2_subdev_get_pad_format (sd, sd_state,0 ); - crop =v4l2_subdev_get_pad_crop (sd, sd_state,0 ); +return 0 ; - *format = fmt->format; - *crop = mode->crop; +error: +v4l2_ctrl_handler_free (ctrl_hdlr); -if (fmt->which ==V4L2_SUBDEV_FORMAT_ACTIVE ) { - imx219->mode = mode; -/* Update limits and set FPS to default */ -__v4l2_ctrl_modify_range (imx219->vblank,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - mode->height,1 , - mode->vts_def - mode->height); -__v4l2_ctrl_s_ctrl (imx219->vblank, - mode->vts_def - mode->height); -/* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def -4 ; - exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? - exposure_max :IMX219_EXPOSURE_DEFAULT ; -__v4l2_ctrl_modify_range (imx219->exposure, - imx219->exposure->minimum, - exposure_max, imx219->exposure->step, - exposure_def); -/* -* Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank -* depends on mode->width only, and is not changeble in any -* way other than changing the mode. -*/ - hblank =IMX219_PPL_DEFAULT - mode->width; -__v4l2_ctrl_modify_range (imx219->hblank, hblank, hblank,1 , - hblank); - } +return ret; +} -return 0 ; +static void imx219_free_controls (struct imx219 *imx219) +{ +v4l2_ctrl_handler_free (imx219->sd.ctrl_handler); } +/* ----------------------------------------------------------------------------- +* Subdev operations +*/ +static int imx219_set_framefmt (struct imx219 *imx219,const struct v4l2_mbus_framefmt *format) {
@@ -748,37 +757,6 @@ static int imx219_set_binning(struct imxreturn -EINVAL ; } -static int imx219_get_selection (struct v4l2_subdev *sd, -struct v4l2_subdev_state *sd_state, -struct v4l2_subdev_selection *sel) -{ -switch (sel->target) { -case V4L2_SEL_TGT_CROP : { - sel->r = *v4l2_subdev_get_pad_crop (sd, sd_state,0 ); -return 0 ; - } - -case V4L2_SEL_TGT_NATIVE_SIZE : - sel->r.top =0 ; - sel->r.left =0 ; - sel->r.width =IMX219_NATIVE_WIDTH ; - sel->r.height =IMX219_NATIVE_HEIGHT ; - -return 0 ; - -case V4L2_SEL_TGT_CROP_DEFAULT : -case V4L2_SEL_TGT_CROP_BOUNDS : - sel->r.top =IMX219_PIXEL_ARRAY_TOP ; - sel->r.left =IMX219_PIXEL_ARRAY_LEFT ; - sel->r.width =IMX219_PIXEL_ARRAY_WIDTH ; - sel->r.height =IMX219_PIXEL_ARRAY_HEIGHT ; - -return 0 ; - } - -return -EINVAL ; -} -static int imx219_configure_lanes (struct imx219 *imx219) { return cci_write(imx219->regmap, IMX219_REG_CSI_LANE_MODE,
@@ -907,6 +885,190 @@ unlock: }/* Power/clock management functions */ +static void imx219_update_pad_format (struct imx219 *imx219, +const struct imx219_mode *mode, +struct v4l2_mbus_framefmt *fmt,u32 code) +{ +/* Bayer order varies with flips */ + fmt->code =imx219_get_format_code (imx219, code); + fmt->width = mode->width; + fmt->height = mode->height; + fmt->field =V4L2_FIELD_NONE ; + fmt->colorspace =V4L2_COLORSPACE_RAW ; + fmt->quantization =V4L2_QUANTIZATION_FULL_RANGE ; + fmt->xfer_func =V4L2_XFER_FUNC_NONE ; +} + +static int imx219_init_cfg (struct v4l2_subdev *sd, +struct v4l2_subdev_state *state) +{ +struct imx219 *imx219 =to_imx219 (sd); +struct v4l2_mbus_framefmt *format; +struct v4l2_rect *crop; + +/* Initialize the format. */ + format =v4l2_subdev_get_pad_format (sd, state,0 ); +imx219_update_pad_format (imx219, &supported_modes[0 ], format, +MEDIA_BUS_FMT_SRGGB10_1X10 ); + +/* Initialize the crop rectangle. */ + crop =v4l2_subdev_get_pad_crop (sd, state,0 ); + crop->top =IMX219_PIXEL_ARRAY_TOP ; + crop->left =IMX219_PIXEL_ARRAY_LEFT ; + crop->width =IMX219_PIXEL_ARRAY_WIDTH ; + crop->height =IMX219_PIXEL_ARRAY_HEIGHT ; + +return 0 ; +} + +static int imx219_enum_mbus_code (struct v4l2_subdev *sd, +struct v4l2_subdev_state *sd_state, +struct v4l2_subdev_mbus_code_enum *code) +{ +struct imx219 *imx219 =to_imx219 (sd); + +if (code->index >= (ARRAY_SIZE (imx219_mbus_formats) /4 )) +return -EINVAL ; + + code->code =imx219_get_format_code (imx219, imx219_mbus_formats[code->index *4 ]); + +return 0 ; +} + +static int imx219_enum_frame_size (struct v4l2_subdev *sd, +struct v4l2_subdev_state *sd_state, +struct v4l2_subdev_frame_size_enum *fse) +{ +struct imx219 *imx219 =to_imx219 (sd); +u32 code; + +if (fse->index >=ARRAY_SIZE (supported_modes)) +return -EINVAL ; + + code =imx219_get_format_code (imx219, fse->code); +if (fse->code != code) +return -EINVAL ; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + +return 0 ; +} + +static int imx219_set_pad_format (struct v4l2_subdev *sd, +struct v4l2_subdev_state *sd_state, +struct v4l2_subdev_format *fmt) +{ +struct imx219 *imx219 =to_imx219 (sd); +const struct imx219_mode *mode; +int exposure_max, exposure_def, hblank; +struct v4l2_mbus_framefmt *format; +struct v4l2_rect *crop; + + mode =v4l2_find_nearest_size (supported_modes, +ARRAY_SIZE (supported_modes), + width, height, + fmt->format.width, fmt->format.height); + +imx219_update_pad_format (imx219, mode, &fmt->format, fmt->format.code); + + format =v4l2_subdev_get_pad_format (sd, sd_state,0 ); + crop =v4l2_subdev_get_pad_crop (sd, sd_state,0 ); + + *format = fmt->format; + *crop = mode->crop; + +if (fmt->which ==V4L2_SUBDEV_FORMAT_ACTIVE ) { + imx219->mode = mode; +/* Update limits and set FPS to default */ +__v4l2_ctrl_modify_range (imx219->vblank,IMX219_VBLANK_MIN , +IMX219_VTS_MAX - mode->height,1 , + mode->vts_def - mode->height); +__v4l2_ctrl_s_ctrl (imx219->vblank, + mode->vts_def - mode->height); +/* Update max exposure while meeting expected vblanking */ + exposure_max = mode->vts_def -4 ; + exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? + exposure_max :IMX219_EXPOSURE_DEFAULT ; +__v4l2_ctrl_modify_range (imx219->exposure, + imx219->exposure->minimum, + exposure_max, imx219->exposure->step, + exposure_def); +/* +* Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank +* depends on mode->width only, and is not changeble in any +* way other than changing the mode. +*/ + hblank =IMX219_PPL_DEFAULT - mode->width; +__v4l2_ctrl_modify_range (imx219->hblank, hblank, hblank,1 , + hblank); + } + +return 0 ; +} + +static int imx219_get_selection (struct v4l2_subdev *sd, +struct v4l2_subdev_state *sd_state, +struct v4l2_subdev_selection *sel) +{ +switch (sel->target) { +case V4L2_SEL_TGT_CROP : { + sel->r = *v4l2_subdev_get_pad_crop (sd, sd_state,0 ); +return 0 ; + } + +case V4L2_SEL_TGT_NATIVE_SIZE : + sel->r.top =0 ; + sel->r.left =0 ; + sel->r.width =IMX219_NATIVE_WIDTH ; + sel->r.height =IMX219_NATIVE_HEIGHT ; + +return 0 ; + +case V4L2_SEL_TGT_CROP_DEFAULT : +case V4L2_SEL_TGT_CROP_BOUNDS : + sel->r.top =IMX219_PIXEL_ARRAY_TOP ; + sel->r.left =IMX219_PIXEL_ARRAY_LEFT ; + sel->r.width =IMX219_PIXEL_ARRAY_WIDTH ; + sel->r.height =IMX219_PIXEL_ARRAY_HEIGHT ; + +return 0 ; + } + +return -EINVAL ; +} + +static const struct v4l2_subdev_core_ops imx219_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx219_video_ops = { + .s_stream = imx219_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx219_pad_ops = { + .init_cfg = imx219_init_cfg, + .enum_mbus_code = imx219_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx219_set_pad_format, + .get_selection = imx219_get_selection, + .enum_frame_size = imx219_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx219_subdev_ops = { + .core = &imx219_core_ops, + .video = &imx219_video_ops, + .pad = &imx219_pad_ops, +}; + + +/* ----------------------------------------------------------------------------- +* Power management +*/ +static int imx219_power_on (struct device *dev) {struct v4l2_subdev *sd =dev_get_drvdata (dev);
@@ -987,6 +1149,10 @@ error:return ret; } +/* ----------------------------------------------------------------------------- +* Probe & remove +*/ +static int imx219_get_regulators (struct imx219 *imx219) {struct i2c_client *client =v4l2_get_subdevdata (&imx219->sd);
@@ -1023,156 +1189,6 @@ static int imx219_identify_module(structreturn 0 ; } -static const struct v4l2_subdev_core_ops imx219_core_ops = { - .subscribe_event = v4l2_ctrl_subdev_subscribe_event, - .unsubscribe_event = v4l2_event_subdev_unsubscribe, -}; - -static const struct v4l2_subdev_video_ops imx219_video_ops = { - .s_stream = imx219_set_stream, -}; - -static const struct v4l2_subdev_pad_ops imx219_pad_ops = { - .init_cfg = imx219_init_cfg, - .enum_mbus_code = imx219_enum_mbus_code, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = imx219_set_pad_format, - .get_selection = imx219_get_selection, - .enum_frame_size = imx219_enum_frame_size, -}; - -static const struct v4l2_subdev_ops imx219_subdev_ops = { - .core = &imx219_core_ops, - .video = &imx219_video_ops, - .pad = &imx219_pad_ops, -}; - - -static unsigned long imx219_get_pixel_rate (struct imx219 *imx219) -{ -return (imx219->lanes ==2 ) ?IMX219_PIXEL_RATE :IMX219_PIXEL_RATE_4LANE ; -} - -/* Initialize control handlers */ -static int imx219_init_controls (struct imx219 *imx219) -{ -struct i2c_client *client =v4l2_get_subdevdata (&imx219->sd); -const struct imx219_mode *mode = &supported_modes[0 ]; -struct v4l2_ctrl_handler *ctrl_hdlr; -struct v4l2_fwnode_device_properties props; -int exposure_max, exposure_def, hblank; -int i, ret; - - ctrl_hdlr = &imx219->ctrl_handler; - ret =v4l2_ctrl_handler_init (ctrl_hdlr,12 ); -if (ret) -return ret; - -/* By default, PIXEL_RATE is read only */ - imx219->pixel_rate =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_PIXEL_RATE , -imx219_get_pixel_rate (imx219), -imx219_get_pixel_rate (imx219),1 , -imx219_get_pixel_rate (imx219)); - - imx219->link_freq = -v4l2_ctrl_new_int_menu (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_LINK_FREQ , -ARRAY_SIZE (imx219_link_freq_menu) -1 ,0 , - (imx219->lanes ==2 ) ? imx219_link_freq_menu : - imx219_link_freq_4lane_menu); -if (imx219->link_freq) - imx219->link_freq->flags |=V4L2_CTRL_FLAG_READ_ONLY ; - -/* Initial vblank/hblank/exposure parameters based on current mode */ - imx219->vblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_VBLANK ,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - mode->height,1 , - mode->vts_def - mode->height); - hblank =IMX219_PPL_DEFAULT - mode->width; - imx219->hblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_HBLANK , hblank, hblank, -1 , hblank); -if (imx219->hblank) - imx219->hblank->flags |=V4L2_CTRL_FLAG_READ_ONLY ; - exposure_max = mode->vts_def -4 ; - exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? - exposure_max :IMX219_EXPOSURE_DEFAULT ; - imx219->exposure =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_EXPOSURE , -IMX219_EXPOSURE_MIN , exposure_max, -IMX219_EXPOSURE_STEP , - exposure_def); - -v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_ANALOGUE_GAIN , -IMX219_ANA_GAIN_MIN ,IMX219_ANA_GAIN_MAX , -IMX219_ANA_GAIN_STEP ,IMX219_ANA_GAIN_DEFAULT ); - -v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_DIGITAL_GAIN , -IMX219_DGTL_GAIN_MIN ,IMX219_DGTL_GAIN_MAX , -IMX219_DGTL_GAIN_STEP ,IMX219_DGTL_GAIN_DEFAULT ); - - imx219->hflip =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_HFLIP ,0 ,1 ,1 ,0 ); -if (imx219->hflip) - imx219->hflip->flags |=V4L2_CTRL_FLAG_MODIFY_LAYOUT ; - - imx219->vflip =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_VFLIP ,0 ,1 ,1 ,0 ); -if (imx219->vflip) - imx219->vflip->flags |=V4L2_CTRL_FLAG_MODIFY_LAYOUT ; - -v4l2_ctrl_new_std_menu_items (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_TEST_PATTERN , -ARRAY_SIZE (imx219_test_pattern_menu) -1 , -0 ,0 , imx219_test_pattern_menu); -for (i =0 ; i <4 ; i++) { -/* -* The assumption is that -* V4L2_CID_TEST_PATTERN_GREENR == V4L2_CID_TEST_PATTERN_RED + 1 -* V4L2_CID_TEST_PATTERN_BLUE == V4L2_CID_TEST_PATTERN_RED + 2 -* V4L2_CID_TEST_PATTERN_GREENB == V4L2_CID_TEST_PATTERN_RED + 3 -*/ -v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops, -V4L2_CID_TEST_PATTERN_RED + i, -IMX219_TESTP_COLOUR_MIN , -IMX219_TESTP_COLOUR_MAX , -IMX219_TESTP_COLOUR_STEP , -IMX219_TESTP_COLOUR_MAX ); -/* The "Solid color" pattern is white by default */ - } - -if (ctrl_hdlr->error) { - ret = ctrl_hdlr->error; -dev_err (&client->dev,"%s control init failed (%d)\n" , - __func__, ret); - goto error; - } - - ret =v4l2_fwnode_device_parse (&client->dev, &props); -if (ret) - goto error; - - ret =v4l2_ctrl_new_fwnode_properties (ctrl_hdlr, &imx219_ctrl_ops, - &props); -if (ret) - goto error; - - imx219->sd.ctrl_handler = ctrl_hdlr; - -return 0 ; - -error: -v4l2_ctrl_handler_free (ctrl_hdlr); - -return ret; -} - -static void imx219_free_controls (struct imx219 *imx219) -{ -v4l2_ctrl_handler_free (imx219->sd.ctrl_handler); -} -static int imx219_check_hwcfg (struct device *dev,struct imx219 *imx219) {struct fwnode_handle *endpoint;
Greg Kroah-Hartman
maintainerPATCH 231/261 media: i2c: imx219: Calculate crop rectangle dynamically
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Laurent Pinchart <[email protected]>
[ Upstream commit 0af46fbc333d1a52c72823d935590410357bab47 ]
Calculate the crop rectangle size and location dynamically when setting the format, instead of storing it in the imx219_mode structure. This removes duplicated information from the mode, to guarantee consistency.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-)
drivers/media/i2c/imx219.c +15 -30
@@ -18,6 +18,7 @@#include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h>
@@ -161,9 +162,6 @@ struct imx219_mode {/* Frame height */ unsigned int height; -/* Analog crop rectangle. */ -struct v4l2_rect crop; -/* V-timing */ unsigned int vts_def;
@@ -365,12 +363,6 @@ static const struct imx219_mode supporte/* 8MPix 15fps mode */ .width = 3280, .height = 2464, - .crop = { - .left = IMX219_PIXEL_ARRAY_LEFT, - .top = IMX219_PIXEL_ARRAY_TOP, - .width = 3280, - .height =2464 - }, .vts_def = 3526, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
@@ -382,12 +374,6 @@ static const struct imx219_mode supporte/* 1080P 30fps cropped */ .width = 1920, .height = 1080, - .crop = { - .left = 688, - .top = 700, - .width = 1920, - .height =1080 - }, .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
@@ -399,12 +385,6 @@ static const struct imx219_mode supporte/* 2x2 binned 30fps mode */ .width = 1640, .height = 1232, - .crop = { - .left = IMX219_PIXEL_ARRAY_LEFT, - .top = IMX219_PIXEL_ARRAY_TOP, - .width = 3280, - .height =2464 - }, .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
@@ -416,12 +396,6 @@ static const struct imx219_mode supporte/* 640x480 30fps mode */ .width = 640, .height = 480, - .crop = { - .left = 1008, - .top = 760, - .width = 1280, - .height =960 - }, .vts_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs),
@@ -966,6 +940,7 @@ static int imx219_set_pad_format(structint exposure_max, exposure_def, hblank;struct v4l2_mbus_framefmt *format;struct v4l2_rect *crop; +unsigned int bin_h, bin_v; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes),
@@ -975,10 +950,20 @@ static int imx219_set_pad_format(structimx219_update_pad_format (imx219, mode, &fmt->format, fmt->format.code); format =v4l2_subdev_get_pad_format (sd, sd_state,0 ); - crop =v4l2_subdev_get_pad_crop (sd, sd_state,0 ); - *format = fmt->format; - *crop = mode->crop; + +/* +* Use binning to maximize the crop rectangle size, and centre it in the +* sensor. +*/ + bin_h =min (IMX219_PIXEL_ARRAY_WIDTH / format->width,2U ); + bin_v =min (IMX219_PIXEL_ARRAY_HEIGHT / format->height,2U ); + + crop =v4l2_subdev_get_pad_crop (sd, sd_state,0 ); + crop->width = format->width * bin_h; + crop->height = format->height * bin_v; + crop->left = (IMX219_NATIVE_WIDTH - crop->width) /2 ; + crop->top = (IMX219_NATIVE_HEIGHT - crop->height) /2 ;if (fmt->which ==V4L2_SUBDEV_FORMAT_ACTIVE ) { imx219->mode = mode;
Greg Kroah-Hartman
maintainerPATCH 232/261 media: i2c: imx219: Rename VTS to FRM_LENGTH
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jai Luthra <[email protected]>
[ Upstream commit 04f78503f99ae7e9887c7fe5e4bc54a7cfb10fe0 ]
The IMX219 datasheet refers to the vertical length + blanking as FRM_LENGTH instead of VTS.
Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
--- drivers/media/i2c/imx219.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-)
drivers/media/i2c/imx219.c +15 -16
@@ -71,9 +71,8 @@#define IMX219_EXPOSURE_MAX 65535/* V_TIMING internal */ -#define IMX219_REG_VTS CCI_REG16(0x0160) -#define IMX219_VTS_MAX 0xffff - +#define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) +#define IMX219_FLL_MAX 0xffff#define IMX219_VBLANK_MIN 32/* HBLANK control - read only */
@@ -163,7 +162,7 @@ struct imx219_mode {unsigned int height;/* V-timing */ -unsigned int vts_def; +unsigned int fll_def;/* Default register values */ struct imx219_reg_list reg_list;
@@ -363,7 +362,7 @@ static const struct imx219_mode supporte/* 8MPix 15fps mode */ .width = 3280, .height = 2464, - .vts_def = 3526, + .fll_def = 3526, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs,
@@ -374,7 +373,7 @@ static const struct imx219_mode supporte/* 1080P 30fps cropped */ .width = 1920, .height = 1080, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs), .regs = mode_1920_1080_regs,
@@ -385,7 +384,7 @@ static const struct imx219_mode supporte/* 2x2 binned 30fps mode */ .width = 1640, .height = 1232, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs), .regs = mode_1640_1232_regs,
@@ -396,7 +395,7 @@ static const struct imx219_mode supporte/* 640x480 30fps mode */ .width = 640, .height = 480, - .vts_def = 1763, + .fll_def = 1763, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_640_480_regs), .regs = mode_640_480_regs,
@@ -518,7 +517,7 @@ static int imx219_set_ctrl(struct v4l2_c imx219->hflip->val | imx219->vflip->val << 1, &ret);break ;case V4L2_CID_VBLANK : -cci_write (imx219->regmap,IMX219_REG_VTS , +cci_write (imx219->regmap,IMX219_REG_FRM_LENGTH_A , format->height + ctrl->val, &ret);break ;case V4L2_CID_TEST_PATTERN_RED :
@@ -593,15 +592,15 @@ static int imx219_init_controls(struct i/* Initial vblank/hblank/exposure parameters based on current mode */ imx219->vblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_VBLANK ,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - mode->height,1 , - mode->vts_def - mode->height); +IMX219_FLL_MAX - mode->height,1 , + mode->fll_def - mode->height); hblank =IMX219_PPL_DEFAULT - mode->width; imx219->hblank =v4l2_ctrl_new_std (ctrl_hdlr, &imx219_ctrl_ops,V4L2_CID_HBLANK , hblank, hblank,1 , hblank);if (imx219->hblank) imx219->hblank->flags |=V4L2_CTRL_FLAG_READ_ONLY ; - exposure_max = mode->vts_def -4 ; + exposure_max = mode->fll_def -4 ; exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? exposure_max :IMX219_EXPOSURE_DEFAULT ; imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
@@ -969,12 +968,12 @@ static int imx219_set_pad_format(struct imx219->mode = mode;/* Update limits and set FPS to default */ __v4l2_ctrl_modify_range (imx219->vblank,IMX219_VBLANK_MIN , -IMX219_VTS_MAX - mode->height,1 , - mode->vts_def - mode->height); +IMX219_FLL_MAX - mode->height,1 , + mode->fll_def - mode->height);__v4l2_ctrl_s_ctrl (imx219->vblank, - mode->vts_def - mode->height); + mode->fll_def - mode->height);/* Update max exposure while meeting expected vblanking */ - exposure_max = mode->vts_def -4 ; + exposure_max = mode->fll_def -4 ; exposure_def = (exposure_max <IMX219_EXPOSURE_DEFAULT ) ? exposure_max :IMX219_EXPOSURE_DEFAULT ; __v4l2_ctrl_modify_range(imx219->exposure,
Greg Kroah-Hartman
maintainerPATCH 233/261 media: imx219: Fix maximum frame length in lines
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sakari Ailus <[email protected]>
[ Upstream commit 2c4f1ba7354312ad2d6e34e70a518a51a9344715 ]
The driver used the maximum frame length in lines value of 0xffff, but the maximum appears to be 0xfffe instead. Fix it.
--- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/media/i2c/imx219.c +1 -1
@@ -72,7 +72,7 @@/* V_TIMING internal */ #define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) -#define IMX219_FLL_MAX 0xffff +#define IMX219_FLL_MAX 0xfffe#define IMX219_VBLANK_MIN 32/* HBLANK control - read only */
Greg Kroah-Hartman
maintainerPATCH 234/261 rxrpc: Fix irq-disabled in local_bh_enable()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Howells <[email protected]>
[ Upstream commit e4d2878369d590bf8455e3678a644e503172eafa ]
The rxrpc_assess_MTU_size() function calls down into the IP layer to find out the MTU size for a route. When accepting an incoming call, this is called from rxrpc_new_incoming_call() which holds interrupts disabled across the code that calls down to it. Unfortunately, the IP layer uses local_bh_enable() which, config dependent, throws a warning if IRQs are enabled:
WARNING: CPU: 1 PID: 5544 at kernel/softirq.c:387 __local_bh_enable_ip+0x43/0xd0 ... RIP: 0010:__local_bh_enable_ip+0x43/0xd0 ... Call Trace: <TASK> rt_cache_route+0x7e/0xa0 rt_set_nexthop.isra.0+0x3b3/0x3f0 __mkroute_output+0x43a/0x460 ip_route_output_key_hash+0xf7/0x140 ip_route_output_flow+0x1b/0x90 rxrpc_assess_MTU_size.isra.0+0x2a0/0x590 rxrpc_new_incoming_peer+0x46/0x120 rxrpc_alloc_incoming_call+0x1b1/0x400 rxrpc_new_incoming_call+0x1da/0x5e0 rxrpc_input_packet+0x827/0x900 rxrpc_io_thread+0x403/0xb60 kthread+0x2f7/0x310 ret_from_fork+0x2a/0x230 ret_from_fork_asm+0x1a/0x30 ... hardirqs last enabled at (23): _raw_spin_unlock_irq+0x24/0x50 hardirqs last disabled at (24): _raw_read_lock_irq+0x17/0x70 softirqs last enabled at (0): copy_process+0xc61/0x2730 softirqs last disabled at (25): rt_add_uncached_list+0x3c/0x90
Fix this by moving the call to rxrpc_assess_MTU_size() out of rxrpc_init_peer() and further up the stack where it can be done without interrupts disabled.
It shouldn't be a problem for rxrpc_new_incoming_call() to do it after the locks are dropped as pmtud is going to be performed by the I/O thread - and we're in the I/O thread at this point.
--- net/rxrpc/ar-internal.h | 1 + net/rxrpc/call_accept.c | 1 + net/rxrpc/peer_object.c | 12 +++++++----- 3 files changed, 9 insertions(+), 5 deletions(-)
net/rxrpc/ar-internal.h +1 -0
@@ -1188,6 +1188,7 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu const struct sockaddr_rxrpc *);struct rxrpc_peer *rxrpc_lookup_peer (struct rxrpc_local *local,struct sockaddr_rxrpc *srx,gfp_t gfp); +void rxrpc_assess_MTU_size (struct rxrpc_local *local,struct rxrpc_peer *peer);struct rxrpc_peer *rxrpc_alloc_peer (struct rxrpc_local *,gfp_t ,enum rxrpc_peer_trace );void rxrpc_new_incoming_peer (struct rxrpc_local *local,struct rxrpc_peer *peer);
net/rxrpc/call_accept.c +1 -0
@@ -407,6 +407,7 @@ bool rxrpc_new_incoming_call(struct rxrpspin_unlock (&rx->incoming_lock);read_unlock_irq (&local->services_lock); +rxrpc_assess_MTU_size (local, call->peer);if (hlist_unhashed (&call->error_link)) {spin_lock_irq (&call->peer->lock);
net/rxrpc/peer_object.c +7 -5
@@ -147,8 +147,7 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu* assess the MTU size for the network interface through which this peer is * reached */ -static void rxrpc_assess_MTU_size (struct rxrpc_local *local, -struct rxrpc_peer *peer) +void rxrpc_assess_MTU_size (struct rxrpc_local *local,struct rxrpc_peer *peer) {struct net *net = local->net;struct dst_entry *dst;
@@ -160,6 +159,8 @@ static void rxrpc_assess_MTU_size(struct#endif peer->if_mtu =1500 ; + peer->mtu = peer->if_mtu; + peer->maxdata = peer->mtu - peer->hdrsize;memset (&fl,0 ,sizeof (fl));switch (peer->srx.transport.family) {
@@ -199,6 +200,9 @@ static void rxrpc_assess_MTU_size(struct peer->if_mtu =dst_mtu (dst);dst_release (dst); + peer->mtu = peer->if_mtu; + peer->maxdata = peer->mtu - peer->hdrsize; +_leave (" [if_mtu %u]" , peer->if_mtu); }
@@ -240,8 +244,6 @@ static void rxrpc_init_peer(struct rxrpc unsigned long hash_key) { peer->hash_key = hash_key; -rxrpc_assess_MTU_size (local, peer); - peer->mtu = peer->if_mtu; peer->rtt_last_req =ktime_get_real ();switch (peer->srx.transport.family) {
@@ -266,7 +268,6 @@ static void rxrpc_init_peer(struct rxrpc } peer->hdrsize +=sizeof (struct rxrpc_wire_header ); - peer->maxdata = peer->mtu - peer->hdrsize; }/*
@@ -285,6 +286,7 @@ static struct rxrpc_peer *rxrpc_create_pif (peer) {memcpy (&peer->srx, srx,sizeof (*srx));rxrpc_init_peer (local, peer, hash_key); +rxrpc_assess_MTU_size (local, peer); }_leave (" = %p" , peer);
Greg Kroah-Hartman
maintainerPATCH 235/261 netfilter: nf_tables: clone set on flush only
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit fb7fb4016300ac622c964069e286dc83166a5d52 ]
Syzbot with fault injection triggered a failing memory allocation with GFP_KERNEL which results in a WARN splat:
iter.err
WARNING: net/netfilter/nf_tables_api.c:845 at nft_map_deactivate+0x34e/0x3c0 net/netfilter/nf_tables_api.c:845, CPU#0: syz.0.17/5992 Modules linked in: CPU: 0 UID: 0 PID: 5992 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2026 RIP: 0010:nft_map_deactivate+0x34e/0x3c0 net/netfilter/nf_tables_api.c:845 Code: 8b 05 86 5a 4e 09 48 3b 84 24 a0 00 00 00 75 62 48 8d 65 d8 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc e8 63 6d fa f7 90 <0f> 0b 90 43 +80 7c 35 00 00 0f 85 23 fe ff ff e9 26 fe ff ff 89 d9 RSP: 0018:ffffc900045af780 EFLAGS: 00010293 RAX: ffffffff89ca45bd RBX: 00000000fffffff4 RCX: ffff888028111e40 RDX: 0000000000000000 RSI: 00000000fffffff4 RDI: 0000000000000000 RBP: ffffc900045af870 R08: 0000000000400dc0 R09: 00000000ffffffff R10: dffffc0000000000 R11: fffffbfff1d141db R12: ffffc900045af7e0 R13: 1ffff920008b5f24 R14: dffffc0000000000 R15: ffffc900045af920 FS: 000055557a6a5500(0000) GS:ffff888125496000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fb5ea271fc0 CR3: 000000003269e000 CR4: 00000000003526f0 Call Trace: <TASK> __nft_release_table+0xceb/0x11f0 net/netfilter/nf_tables_api.c:12115 nft_rcv_nl_event+0xc25/0xdb0 net/netfilter/nf_tables_api.c:12187 notifier_call_chain+0x19d/0x3a0 kernel/notifier.c:85 blocking_notifier_call_chain+0x6a/0x90 kernel/notifier.c:380 netlink_release+0x123b/0x1ad0 net/netlink/af_netlink.c:761 __sock_release net/socket.c:662 [inline] sock_close+0xc3/0x240 net/socket.c:1455
Restrict set clone to the flush set command in the preparation phase. Add NFT_ITER_UPDATE_CLONE and use it for this purpose, update the rbtree and pipapo backends to only clone the set when this iteration type is used.
As for the existing NFT_ITER_UPDATE type, update the pipapo backend to use the existing set clone if available, otherwise use the existing set representation. After this update, there is no need to clone a set that is being deleted, this includes bound anonymous set.
An alternative approach to NFT_ITER_UPDATE_CLONE is to add a .clone interface and call it from the flush set path.
--- include/net/netfilter/nf_tables.h | 2 ++ net/netfilter/nf_tables_api.c | 10 +++++++++- net/netfilter/nft_set_pipapo.c | 11 +++++++++-- 3 files changed, 20 insertions(+), 3 deletions(-)
include/net/netfilter/nf_tables.h +2 -0
@@ -302,11 +302,13 @@ struct nft_set_elem {* * @NFT_ITER_READ: read-only iteration over set elements * @NFT_ITER_UPDATE: iteration under mutex to update set element state +* @NFT_ITER_UPDATE_CLONE: clone set before iteration under mutex to update element */ enum nft_iter_type {NFT_ITER_UNSPEC ,NFT_ITER_READ ,NFT_ITER_UPDATE , +NFT_ITER_UPDATE_CLONE , };struct nft_set ;
net/netfilter/nf_tables_api.c +9 -1
@@ -655,6 +655,11 @@ static void nft_map_catchall_deactivate( } } +/* Use NFT_ITER_UPDATE iterator even if this may be called from the preparation +* phase, the set clone might already exist from a previous command, or it might +* be a set that is going away and does not require a clone. The netns and +* netlink release paths also need to work on the live set. +*/ static void nft_map_deactivate (const struct nft_ctx *ctx,struct nft_set *set) {struct nft_set_iter iter = {
@@ -7396,9 +7401,12 @@ static int nft_set_catchall_flush(conststatic int nft_set_flush (struct nft_ctx *ctx,struct nft_set *set,u8 genmask) { +/* The set backend might need to clone the set, do it now from the +* preparation phase, use NFT_ITER_UPDATE_CLONE iterator type. +*/ struct nft_set_iter iter = { .genmask = genmask, - .type =NFT_ITER_UPDATE , + .type =NFT_ITER_UPDATE_CLONE , .fn = nft_setelem_flush, };
net/netfilter/nft_set_pipapo.c +9 -2
@@ -2199,13 +2199,20 @@ static void nft_pipapo_walk(const structconst struct nft_pipapo_match *m;switch (iter->type) { -case NFT_ITER_UPDATE : +case NFT_ITER_UPDATE_CLONE : m =pipapo_maybe_clone (set);if (!m) { iter->err = -ENOMEM ;return ; } - +nft_pipapo_do_walk (ctx, set, m, iter); +break ; +case NFT_ITER_UPDATE : +if (priv->clone) + m = priv->clone; +else + m =rcu_dereference_protected (priv->match, +nft_pipapo_transaction_mutex_held (set));nft_pipapo_do_walk (ctx, set, m, iter);break ;case NFT_ITER_READ :
Greg Kroah-Hartman
maintainerPATCH 236/261 media: v4l: async: Set owner for async sub-devices
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sakari Ailus <[email protected]>
[ Upstream commit 8a718752f5c339137c5b05e54f116cd26d5a4143 ]
Set the owner field of the async sub-devices by making v4l2_async_register_subdev() a macro and obtaining THIS_MODULE that way.
Stable-dep-of: 06cb687a5132 ("media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor()")
--- drivers/media/v4l2-core/v4l2-async.c | 8 ++++++-- include/media/v4l2-async.h | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-)
drivers/media/v4l2-core/v4l2-async.c +6 -2
@@ -798,7 +798,7 @@ v4l2_async_connection_unique(struct v4l2 }EXPORT_SYMBOL_GPL (v4l2_async_connection_unique); -int v4l2_async_register_subdev (struct v4l2_subdev *sd) +int __v4l2_async_register_subdev (struct v4l2_subdev *sd,struct module *module) {struct v4l2_async_notifier *subdev_notifier;struct v4l2_async_notifier *notifier;
@@ -822,6 +822,8 @@ int v4l2_async_register_subdev(struct v4return -EINVAL ; } + sd->owner = module; +mutex_lock (&list_lock);list_for_each_entry (notifier, ¬ifier_list, notifier_entry) {
@@ -869,9 +871,11 @@ err_unbind_one: err_unlock:mutex_unlock (&list_lock); + sd->owner =NULL ; +return ret; } -EXPORT_SYMBOL (v4l2_async_register_subdev); +EXPORT_SYMBOL (__v4l2_async_register_subdev);void v4l2_async_unregister_subdev (struct v4l2_subdev *sd) {
include/media/v4l2-async.h +3 -1
@@ -310,7 +310,9 @@ void v4l2_async_nf_cleanup(struct v4l2_a* * @sd: pointer to &struct v4l2_subdev */ -int v4l2_async_register_subdev (struct v4l2_subdev *sd); +#define v4l2_async_register_subdev (sd) \ + __v4l2_async_register_subdev(sd, THIS_MODULE) +int __v4l2_async_register_subdev (struct v4l2_subdev *sd,struct module *module);/** * v4l2_async_register_subdev_sensor - registers a sensor sub-device to the
Greg Kroah-Hartman
maintainerPATCH 237/261 media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mirela Rabulea <[email protected]>
[ Upstream commit 06cb687a5132fcffe624c0070576ab852ac6b568 ]
The v4l2 helper v4l2_async_register_subdev_sensor() calls v4l2_async_register_subdev(), which is a macro that expands to __v4l2_async_register_subdev(sd,THIS_MODULE). Since the macro is expanded inside v4l2-fwnode.c, THIS_MODULE resolves to the v4l2-fwnode module rather than the sensor driver module that originally set sd->owner. When v4l2-fwnode is built-in, THIS_MODULE evaluates to NULL, which then overwrites the sensor driver's owner with NULL.
This causes the problem that the sensor module's reference count is never incremented during async registration, so the module can be removed while the subdevice is still in use by a notifier (e.g., a CSI-2 receiver bridge driver).
Fix this by renaming v4l2_async_register_subdev_sensor() to __v4l2_async_register_subdev_sensor() with an added explicit module argument and introducing a wrapper macro:
#define v4l2_async_register_subdev_sensor(sd) \
__v4l2_async_register_subdev_sensor(sd, THIS_MODULE)This ensures the sensor driver module is properly referenced even when the sensor driver does not init the owner field before calling v4l2_async_register_subdev_sensor() and prevents premature module removal.
--- drivers/media/v4l2-core/v4l2-fwnode.c | 6 +++--- include/media/v4l2-async.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-)
drivers/media/v4l2-core/v4l2-fwnode.c +3 -3
@@ -1203,7 +1203,7 @@ v4l2_async_nf_parse_fwnode_sensor(structreturn 0 ; } -int v4l2_async_register_subdev_sensor (struct v4l2_subdev *sd) +int __v4l2_async_register_subdev_sensor (struct v4l2_subdev *sd,struct module *module) {struct v4l2_async_notifier *notifier;int ret;
@@ -1229,7 +1229,7 @@ int v4l2_async_register_subdev_sensor(stif (ret <0 ) goto out_cleanup; - ret =v4l2_async_register_subdev (sd); + ret =__v4l2_async_register_subdev (sd, module);if (ret <0 ) goto out_unregister;
@@ -1247,7 +1247,7 @@ out_cleanup:return ret; } -EXPORT_SYMBOL_GPL (v4l2_async_register_subdev_sensor); +EXPORT_SYMBOL_GPL (__v4l2_async_register_subdev_sensor);MODULE_LICENSE ("GPL" );MODULE_AUTHOR ("Sakari Ailus <[email protected]>" );
include/media/v4l2-async.h +3 -1
@@ -333,8 +333,10 @@ int __v4l2_async_register_subdev(struct* An error is returned if the module is no longer loaded on any attempts * to register it. */ +#define v4l2_async_register_subdev_sensor (sd) \ + __v4l2_async_register_subdev_sensor(sd, THIS_MODULE)int __must_check -v4l2_async_register_subdev_sensor (struct v4l2_subdev *sd); +__v4l2_async_register_subdev_sensor (struct v4l2_subdev *sd,struct module *module);/** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous
Greg Kroah-Hartman
maintainerPATCH 238/261 wifi: ath6kl: fix use-after-free in aggr_reset_state()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Hodges <[email protected]>
[ Upstream commit ba7debb4dd6427386862220e8335a53a4bfc235d ]
The aggr_reset_state() function uses timer_delete() (non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers like aggr_module_destroy().
If the timer callback (aggr_timeout) is executing when aggr_reset_state() is called, the callback will continue to access aggr_conn fields like rx_tid[] and stat[] which may be freed immediately after by kfree(aggr_info->aggr_conn) in aggr_module_destroy().
Additionally, the timer callback can re-arm itself via mod_timer() while aggr_reset_state() is running, creating a more complex race condition.
Use timer_delete_sync() instead to ensure any running timer callback has completed before returning.
--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/ath/ath6kl/txrx.c +1 -1
@@ -1829,7 +1829,7 @@ void aggr_reset_state(struct aggr_info_creturn ;if (aggr_conn->timer_scheduled) { -del_timer (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer); aggr_conn->timer_scheduled = false; }
Greg Kroah-Hartman
maintainerPATCH 239/261 wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW)
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gokul Sivakumar <[email protected]>
[ Upstream commit 74e2ef72bd4b25ce21c8f309d4f5b91b5df9ff5b ]
Cypress(Infineon) is not the vendor for this 43752 SDIO WLAN chip, and so has not officially released any firmware binary for it. It is incorrect to maintain this WLAN chip with firmware vendor ID as "CYW". So relabel the chip's firmware Vendor ID as "WCC" as suggested by the maintainer.
Stable-dep-of: 29ab31f3f271 ("wifi: brcmfmac: set F2 blocksize to 256 for BCM43752")
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 8 ++++---- drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 2 +- include/linux/mmc/sdio_ids.h | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +1 -1
@@ -991,9 +991,9 @@ static const struct sdio_device_id brcmfBRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_4354 ,WCC ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_4356 ,WCC ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_4359 ,WCC ), +BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_43752 ,WCC ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 ,CYW ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012 ,CYW ), -BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752 ,CYW ),BRCMF_SDIO_DEVICE (SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359 ,CYW ), CYW_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439, CYW), { /* end: all zeroes */ }
drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +2 -2
@@ -738,7 +738,7 @@ static u32 brcmf_chip_tcm_rambase(structcase BRCM_CC_4364_CHIP_ID :case CY_CC_4373_CHIP_ID :return 0x160000 ; -case CY_CC_43752_CHIP_ID : +case BRCM_CC_43752_CHIP_ID :case BRCM_CC_4377_CHIP_ID :return 0x170000 ;case BRCM_CC_4378_CHIP_ID :
@@ -1465,7 +1465,7 @@ bool brcmf_chip_sr_capable(struct brcmf_ reg = chip->ops->read32 (chip->ctx, addr);return (reg &CC_SR_CTL0_ENABLE_MASK ) !=0 ;case BRCM_CC_4359_CHIP_ID : -case CY_CC_43752_CHIP_ID : +case BRCM_CC_43752_CHIP_ID :case CY_CC_43012_CHIP_ID : addr =CORE_CC_REG (pmu->base, retention_ctl); reg = chip->ops->read32 (chip->ctx, addr);
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +4 -4
@@ -654,10 +654,10 @@ static const struct brcmf_firmware_mappiBRCMF_FW_ENTRY (BRCM_CC_4354_CHIP_ID ,0xFFFFFFFF ,4354 ),BRCMF_FW_ENTRY (BRCM_CC_4356_CHIP_ID ,0xFFFFFFFF ,4356 ),BRCMF_FW_ENTRY (BRCM_CC_4359_CHIP_ID ,0xFFFFFFFF ,4359 ), +BRCMF_FW_ENTRY (BRCM_CC_43752_CHIP_ID ,0xFFFFFFFF ,43752 ),BRCMF_FW_ENTRY (CY_CC_4373_CHIP_ID ,0xFFFFFFFF ,4373 ),BRCMF_FW_ENTRY (CY_CC_43012_CHIP_ID ,0xFFFFFFFF ,43012 ), BRCMF_FW_ENTRY(CY_CC_43439_CHIP_ID, 0xFFFFFFFF, 43439), - BRCMF_FW_ENTRY(CY_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752) };#define TXCTL_CREDITS 2
@@ -3425,8 +3425,8 @@ err:static bool brcmf_sdio_aos_no_decode (struct brcmf_sdio *bus) { -if (bus->ci->chip ==CY_CC_43012_CHIP_ID || - bus->ci->chip ==CY_CC_43752_CHIP_ID ) +if (bus->ci->chip ==BRCM_CC_43752_CHIP_ID || + bus->ci->chip ==CY_CC_43012_CHIP_ID )return true;else return false;
@@ -4274,8 +4274,8 @@ static void brcmf_sdio_firmware_callback bus->hostintmask, NULL);switch (sdiod->func1->device) { +case SDIO_DEVICE_ID_BROADCOM_43752 :case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 : -case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752 :brcmf_dbg (INFO ,"set F2 watermark to 0x%x*4 bytes\n" ,CY_4373_F2_WATERMARK ); brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +1 -1
@@ -52,13 +52,13 @@#define BRCM_CC_43664_CHIP_ID 43664#define BRCM_CC_43666_CHIP_ID 43666#define BRCM_CC_4371_CHIP_ID 0x4371 +#define BRCM_CC_43752_CHIP_ID 43752#define BRCM_CC_4377_CHIP_ID 0x4377#define BRCM_CC_4378_CHIP_ID 0x4378#define BRCM_CC_4387_CHIP_ID 0x4387#define CY_CC_4373_CHIP_ID 0x4373#define CY_CC_43012_CHIP_ID 43012#define CY_CC_43439_CHIP_ID 43439 -#define CY_CC_43752_CHIP_ID 43752/* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e
include/linux/mmc/sdio_ids.h +1 -1
@@ -76,7 +76,7 @@#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6#define SDIO_DEVICE_ID_BROADCOM_43439 0xa9af#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf -#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752 0xaae8 +#define SDIO_DEVICE_ID_BROADCOM_43752 0xaae8#define SDIO_VENDOR_ID_CYPRESS 0x04b4#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439 0xbd3d
Greg Kroah-Hartman
maintainerPATCH 240/261 wifi: brcmfmac: set F2 blocksize to 256 for BCM43752
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: LiangCheng Wang <[email protected]>
[ Upstream commit 29ab31f3f27157648f2f7e6d5e1fd9792fdf0614 ]
The BCM43752 is not reliable with the default 512-byte SDIO function 2 block size: on an i.MX8MP board with an AMPAK AP6275S module at SDR104 / 200 MHz, an iperf TX stress test kills WLAN within seconds:
mmc_submit_one: CMD53 sg block write failed -84
brcmf_sdio_dpc: failed backplane access over SDIO, halting operation
Commit d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") set up the 43752 like the 4373 for the F2 watermark but missed the F2 block size, which the 4373 limits to 256 bytes. The vendor driver (bcmdhd) also programs a 256-byte F2 block size for this chip and runs the same hardware without errors.
Group the 43752 with the 4373, matching the F2 watermark handling. With this change a 10-minute bidirectional iperf3 soak completes with zero SDIO errors at ~270 Mbit/s in each direction.
Backporting note: kernels before v6.18 name this id SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, so on those trees the case label added by this patch must be adjusted to that name. Cherry-picking the rename commit 74e2ef72bd4b ("wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW)") first is not a clean
alternative: on trees before v6.17 its context collides with the 43751 additions, and trees before v6.2 lack the FWVID framework it touches.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +1 -0
@@ -906,6 +906,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_return ret; }switch (sdiodev->func2->device) { +case SDIO_DEVICE_ID_BROADCOM_43752 :case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 : f2_blksz =SDIO_4373_FUNC2_BLOCKSIZE ;break ;
Greg Kroah-Hartman
maintainerPATCH 241/261 wifi: brcmfmac: drain bus_reset work on device removal
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit 43b25879f004c98defa2776bedc6ca4763c51945 ]
brcmf_fw_crashed() and the debugfs "reset" entry both schedule drvr->bus_reset, whose callback recovers drvr through container_of() and dereferences it. The removal path frees drvr (brcmf_free -> wiphy_free) without draining the work, so a bus_reset callback pending or running during removal can outlive drvr.
Cancellation cannot live in brcmf_detach() or brcmf_free(): the work callback reaches teardown through the bus .reset op (PCIe brcmf_pcie_reset -> brcmf_detach; SDIO brcmf_sdio_bus_reset -> brcmf_sdiod_remove -> brcmf_free), so cancelling there would wait for the running work and deadlock.
Add a per-bus mutex (bus_reset_lock) and route all arming through brcmf_bus_schedule_reset(), which under the lock skips when the bus is marked removing. Each bus remove entry calls brcmf_bus_cancel_reset_work(), which under the same lock sets removing and cancels the work. Holding the mutex across cancel_work_sync() makes the set-removing + drain step atomic. Every producer reaches the arming path from process context -- the PCIe firmware-halt notification runs in the threaded IRQ handler (brcmf_pcie_isr_thread) and the SDIO hostmail path runs from the data workqueue -- so the mutex is taken only in sleepable contexts. Where applicable the remove entry first stops the firmware-crash producer: on PCIe mask the mailbox and synchronize_irq; on SDIO unregister the bus interrupt and cancel the data worker, which also reports firmware halts through brcmf_fw_crashed(). The mutex is initialized at bus allocation. The SDIO suspend power-off path frees drvr through the same brcmf_sdiod_remove() and takes the same lock; resume re-allows the work only on a successful re-probe.
Also guard brcmf_fw_crashed() against a NULL bus_if/drvr: it can fire before brcmf_attach() wires up drvr, and it dereferences drvr (bphy_err/brcmf_dev_coredump) before reaching the arming gate.
The bus_reset work is shared across buses, so the drain is applied to every remove path: PCIe (the .reset op introduced by the Fixes commit), SDIO (arms the same work through brcmf_fw_crashed()), and USB (via the debugfs "reset" entry). cancel_work_sync() drains a running or pending bus_reset work item before removal frees drvr, and patch 1/2 makes the scratch-buffer release safe when reset teardown has already released those DMA buffers.
This patch fixes the lifetime of the bus_reset work item itself. It does not attempt to address the separate, pre-existing lifetime of the asynchronous firmware completion started by the PCIe reset path. That callback needs its own lifetime/ownership protocol and is being tracked separately.
This issue was found by an in-house static analysis tool.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 13 +++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 46 ++++++++++++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h | 1 drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 3 7 files changed, 77 insertions(+), 4 deletions(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +13 -0
@@ -1065,6 +1065,7 @@ static int brcmf_ops_sdio_probe(struct s bus_if =kzalloc (sizeof (struct brcmf_bus ),GFP_KERNEL );if (!bus_if)return -ENOMEM ; +mutex_init (&bus_if->bus_reset_lock); sdiodev =kzalloc (sizeof (struct brcmf_sdio_dev ),GFP_KERNEL );if (!sdiodev) {kfree (bus_if);
@@ -1126,6 +1127,14 @@ static void brcmf_ops_sdio_remove(structif (func->num !=1 )return ; +/* Drain bus_reset before the shared brcmf_sdiod_remove() +* teardown, which the SDIO reset callback also reaches. The +* data worker can arm bus_reset via brcmf_fw_crashed(); cancel +* it first. +*/ +brcmf_sdio_cancel_datawork (sdiodev->bus); +brcmf_bus_cancel_reset_work (bus_if); +/* only proceed with rest of cleanup if func 1 */ brcmf_sdiod_remove (sdiodev);
@@ -1200,6 +1209,8 @@ static int brcmf_ops_sdio_suspend(struct }else {/* power will be cut so remove device, probe again in resume */ brcmf_sdiod_intr_unregister (sdiodev); +brcmf_sdio_cancel_datawork (sdiodev->bus); +brcmf_bus_cancel_reset_work (bus_if); ret =brcmf_sdiod_remove (sdiodev);if (ret)brcmf_err ("Failed to remove device on suspend\n" );
@@ -1225,6 +1236,8 @@ static int brcmf_ops_sdio_resume(struct ret =brcmf_sdiod_probe (sdiodev);if (ret)brcmf_err ("Failed to probe device on resume\n" ); +else +brcmf_bus_allow_reset_work (bus_if); }else {if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)disable_irq_wake (sdiodev->settings->bus.sdio.oob_irq_nr);
drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +6 -0
@@ -9,6 +9,7 @@#include <linux/kernel.h> #include <linux/firmware.h> #include <linux/device.h> +#include <linux/mutex.h> #include "debug.h" /* IDs of the 6 default common rings of msgbuf protocol */
@@ -179,6 +180,8 @@ struct brcmf_bus {enum brcmf_fwvendor fwvid;bool always_use_fws_queue;bool wowl_supported; +bool removing;/* device removal in progress; quiesce async work */ +struct mutex bus_reset_lock;const struct brcmf_bus_ops *ops;struct brcmf_bus_msgbuf *msgbuf;
@@ -186,6 +189,9 @@ struct brcmf_bus {struct list_head list; }; +void brcmf_bus_cancel_reset_work (struct brcmf_bus *bus_if); +void brcmf_bus_allow_reset_work (struct brcmf_bus *bus_if); +/* * callback wrappers */
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +42 -4
69 lines not on this page · raw message
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +6 -0
@@ -2467,6 +2467,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, c ret = -ENOMEM ; goto fail; } +mutex_init (&bus->bus_reset_lock); bus->msgbuf =kzalloc (sizeof (*bus->msgbuf),GFP_KERNEL );if (!bus->msgbuf) { ret = -ENOMEM ;
@@ -2552,6 +2553,11 @@ brcmf_pcie_remove(struct pci_dev *pdev)if (devinfo->ci)brcmf_pcie_intr_disable (devinfo); +if (devinfo->irq_allocated) +synchronize_irq (pdev->irq); + +brcmf_bus_cancel_reset_work (bus); +brcmf_detach (&pdev->dev);brcmf_free (&pdev->dev);
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +6 -0
@@ -4550,6 +4550,12 @@ fail:return NULL ; } +void brcmf_sdio_cancel_datawork (struct brcmf_sdio *bus) +{ +if (bus) +cancel_work_sync (&bus->datawork); +} +/* Detach and free everything */ void brcmf_sdio_remove (struct brcmf_sdio *bus) {
drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +1 -0
@@ -361,6 +361,7 @@ int brcmf_sdiod_remove(struct brcmf_sdiostruct brcmf_sdio *brcmf_sdio_probe (struct brcmf_sdio_dev *sdiodev);void brcmf_sdio_remove (struct brcmf_sdio *bus);void brcmf_sdio_isr (struct brcmf_sdio *bus,bool in_isr); +void brcmf_sdio_cancel_datawork (struct brcmf_sdio *bus);void brcmf_sdio_wd_timer (struct brcmf_sdio *bus,bool active);void brcmf_sdio_wowl_config (struct device *dev,bool enabled);
drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +3 -0
@@ -1261,6 +1261,7 @@ static int brcmf_usb_probe_cb(struct brc ret = -ENOMEM ; goto fail; } +mutex_init (&bus->bus_reset_lock); bus->dev = dev; bus_pub->bus = bus;
@@ -1327,6 +1328,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usbreturn ;brcmf_dbg (USB ,"Enter, bus_pub %p\n" , devinfo); +brcmf_bus_cancel_reset_work (devinfo->bus_pub.bus); +brcmf_detach (devinfo->dev);brcmf_free (devinfo->dev);kfree (devinfo->bus_pub.bus);
Greg Kroah-Hartman
maintainerPATCH 242/261 sysctl: treewide: constify ctl_table_header::ctl_table_arg
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Weißschuh <[email protected]>
[ Upstream commit bfa858f220ab8c950dd3e1310fee61950d0ecdae ]
To be able to constify instances of struct ctl_tables it is necessary to remove ways through which non-const versions are exposed from the sysctl core.
One of these is the ctl_table_arg member of struct ctl_table_header.
Constify this reference as a prerequisite for the full constification of struct ctl_table instances.
No functional change.
Stable-dep-of: f8d5e7846025 ("sctp: avoid auth_enable sysctl UAF during netns teardown")
--- drivers/net/vrf.c | 2 +- include/linux/sysctl.h | 2 +- ipc/ipc_sysctl.c | 2 +- ipc/mq_sysctl.c | 2 +- kernel/ucount.c | 2 +- net/ax25/sysctl_net_ax25.c | 2 +- net/bridge/br_netfilter_hooks.c | 2 +- net/core/sysctl_net_core.c | 2 +- net/ieee802154/6lowpan/reassembly.c | 2 +- net/ipv4/devinet.c | 2 +- net/ipv4/ip_fragment.c | 2 +- net/ipv4/route.c | 2 +- net/ipv4/sysctl_net_ipv4.c | 2 +- net/ipv4/xfrm4_policy.c | 2 +- net/ipv6/addrconf.c | 2 +- net/ipv6/netfilter/nf_conntrack_reasm.c | 2 +- net/ipv6/reassembly.c | 2 +- net/ipv6/sysctl_net_ipv6.c | 6 +++--- net/ipv6/xfrm6_policy.c | 2 +- net/mpls/af_mpls.c | 4 ++-- net/mptcp/ctrl.c | 2 +- net/netfilter/nf_conntrack_standalone.c | 2 +- net/netfilter/nf_log.c | 2 +- net/sctp/sysctl.c | 2 +- net/smc/smc_sysctl.c | 2 +- net/unix/sysctl_net_unix.c | 2 +- net/xfrm/xfrm_sysctl.c | 2 +- 27 files changed, 30 insertions(+), 30 deletions(-)
drivers/net/vrf.c +1 -1
@@ -1983,7 +1983,7 @@ static int vrf_netns_init_sysctl(structstatic void vrf_netns_exit_sysctl (struct net *net) {struct netns_vrf *nn_vrf =net_generic (net, vrf_net_id); -struct ctl_table *table; +const struct ctl_table *table; table = nn_vrf->ctl_hdr->ctl_table_arg;unregister_net_sysctl_table (nn_vrf->ctl_hdr);
include/linux/sysctl.h +1 -1
@@ -182,7 +182,7 @@ struct ctl_table_header {struct rcu_head rcu; };struct completion *unregistering; -struct ctl_table *ctl_table_arg; +const struct ctl_table *ctl_table_arg;struct ctl_table_root *root;struct ctl_table_set *set;struct ctl_dir *parent;
ipc/ipc_sysctl.c +1 -1
@@ -306,7 +306,7 @@ bool setup_ipc_sysctls(struct ipc_namespvoid retire_ipc_sysctls (struct ipc_namespace *ns) { -struct ctl_table *tbl; +const struct ctl_table *tbl; tbl = ns->ipc_sysctls->ctl_table_arg;unregister_sysctl_table (ns->ipc_sysctls);
ipc/mq_sysctl.c +1 -1
@@ -159,7 +159,7 @@ bool setup_mq_sysctls(struct ipc_namespavoid retire_mq_sysctls (struct ipc_namespace *ns) { -struct ctl_table *tbl; +const struct ctl_table *tbl; tbl = ns->mq_sysctls->ctl_table_arg;unregister_sysctl_table (ns->mq_sysctls);
kernel/ucount.c +1 -1
@@ -119,7 +119,7 @@ bool setup_userns_sysctls(struct user_navoid retire_userns_sysctls (struct user_namespace *ns) {#ifdef CONFIG_SYSCTL -struct ctl_table *tbl; +const struct ctl_table *tbl; tbl = ns->sysctls->ctl_table_arg;unregister_sysctl_table (ns->sysctls);
net/ax25/sysctl_net_ax25.c +1 -1
@@ -171,7 +171,7 @@ int ax25_register_dev_sysctl(ax25_dev *avoid ax25_unregister_dev_sysctl (ax25_dev *ax25_dev) {struct ctl_table_header *header = ax25_dev->sysheader; -struct ctl_table *table; +const struct ctl_table *table;if (header) { ax25_dev->sysheader =NULL ;
net/bridge/br_netfilter_hooks.c +1 -1
@@ -1290,7 +1290,7 @@ static int br_netfilter_sysctl_init_net(static void br_netfilter_sysctl_exit_net (struct net *net,struct brnf_net *brnet) { -struct ctl_table *table = brnet->ctl_hdr->ctl_table_arg; +const struct ctl_table *table = brnet->ctl_hdr->ctl_table_arg;unregister_net_sysctl_table (brnet->ctl_hdr);if (!net_eq (net, &init_net))
net/core/sysctl_net_core.c +1 -1
@@ -741,7 +741,7 @@ err_dup: static __net_exit void sysctl_core_net_exit(struct net *net) { -struct ctl_table *tbl; +const struct ctl_table *tbl; tbl = net->core.sysctl_hdr->ctl_table_arg;unregister_net_sysctl_table (net->core.sysctl_hdr);
net/ieee802154/6lowpan/reassembly.c +1 -1
@@ -399,7 +399,7 @@ err_alloc: static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net) { -struct ctl_table *table; +const struct ctl_table *table;struct netns_ieee802154_lowpan *ieee802154_lowpan =net_ieee802154_lowpan (net);
net/ipv4/devinet.c +1 -1
@@ -2793,7 +2793,7 @@ err_alloc_all: static __net_exit void devinet_exit_net(struct net *net) {#ifdef CONFIG_SYSCTL -struct ctl_table *tbl; +const struct ctl_table *tbl; tbl = net->ipv4.forw_hdr->ctl_table_arg;unregister_net_sysctl_table (net->ipv4.forw_hdr);
net/ipv4/ip_fragment.c +1 -1
@@ -632,7 +632,7 @@ err_alloc: static void __net_exit ip4_frags_ns_ctl_unregister(struct net *net) { -struct ctl_table *table; +const struct ctl_table *table; table = net->ipv4.frags_hdr->ctl_table_arg;unregister_net_sysctl_table (net->ipv4.frags_hdr);
net/ipv4/route.c +1 -1
@@ -3621,7 +3621,7 @@ err_dup: static __net_exit void sysctl_route_net_exit(struct net *net) { -struct ctl_table *tbl; +const struct ctl_table *tbl; tbl = net->ipv4.route_hdr->ctl_table_arg;unregister_net_sysctl_table (net->ipv4.route_hdr);
net/ipv4/sysctl_net_ipv4.c +1 -1
@@ -1564,7 +1564,7 @@ err_alloc: static __net_exit void ipv4_sysctl_exit_net(struct net *net) { -struct ctl_table *table; +const struct ctl_table *table; table = net->ipv4.ipv4_hdr->ctl_table_arg;unregister_net_sysctl_table (net->ipv4.ipv4_hdr);
net/ipv4/xfrm4_policy.c +1 -1
@@ -182,7 +182,7 @@ err_alloc: static __net_exit void xfrm4_net_sysctl_exit(struct net *net) { -struct ctl_table *table; +const struct ctl_table *table;if (!net->ipv4.xfrm4_hdr)return ;
net/ipv6/addrconf.c +1 -1
@@ -7251,7 +7251,7 @@ out:static void __addrconf_sysctl_unregister (struct net *net,struct ipv6_devconf *p,int ifindex) { -struct ctl_table *table; +const struct ctl_table *table;if (!p->sysctl_header)return ;
net/ipv6/netfilter/nf_conntrack_reasm.c +1 -1
@@ -105,7 +105,7 @@ err_alloc: static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net) {struct nft_ct_frag6_pernet *nf_frag =nf_frag_pernet (net); -struct ctl_table *table; +const struct ctl_table *table; table = nf_frag->nf_frag_frags_hdr->ctl_table_arg;unregister_net_sysctl_table (nf_frag->nf_frag_frags_hdr);
net/ipv6/reassembly.c +1 -1
@@ -487,7 +487,7 @@ err_alloc: static void __net_exit ip6_frags_ns_sysctl_unregister(struct net *net) { -struct ctl_table *table; +const struct ctl_table *table; table = net->ipv6.sysctl.frags_hdr->ctl_table_arg;unregister_net_sysctl_table (net->ipv6.sysctl.frags_hdr);
net/ipv6/sysctl_net_ipv6.c +3 -3
@@ -313,9 +313,9 @@ out_ipv6_table: static void __net_exit ipv6_sysctl_net_exit(struct net *net) { -struct ctl_table *ipv6_table; -struct ctl_table *ipv6_route_table; -struct ctl_table *ipv6_icmp_table; +const struct ctl_table *ipv6_table; +const struct ctl_table *ipv6_route_table; +const struct ctl_table *ipv6_icmp_table; ipv6_table = net->ipv6.sysctl.hdr->ctl_table_arg; ipv6_route_table = net->ipv6.sysctl.route_hdr->ctl_table_arg;
net/ipv6/xfrm6_policy.c +1 -1
@@ -229,7 +229,7 @@ err_alloc: static void __net_exit xfrm6_net_sysctl_exit(struct net *net) { -struct ctl_table *table; +const struct ctl_table *table;if (!net->ipv6.sysctl.xfrm6_hdr)return ;
net/mpls/af_mpls.c +2 -2
@@ -1422,7 +1422,7 @@ static void mpls_dev_sysctl_unregister(s struct mpls_dev *mdev) {struct net *net =dev_net (dev); -struct ctl_table *table; +const struct ctl_table *table;if (!mdev->sysctl)return ;
@@ -2692,7 +2692,7 @@ static void mpls_net_exit(struct net *ne {struct mpls_route __rcu **platform_label;size_t platform_labels; -struct ctl_table *table; +const struct ctl_table *table;unsigned int index; table = net->mpls.ctl->ctl_table_arg;
net/mptcp/ctrl.c +1 -1
@@ -218,7 +218,7 @@ err_alloc:static void mptcp_pernet_del_table (struct mptcp_pernet *pernet) { -struct ctl_table *table = pernet->ctl_table_hdr->ctl_table_arg; +const struct ctl_table *table = pernet->ctl_table_hdr->ctl_table_arg;unregister_net_sysctl_table (pernet->ctl_table_hdr);
net/netfilter/nf_conntrack_standalone.c +1 -1
@@ -1113,7 +1113,7 @@ out_unregister_netfilter:static void nf_conntrack_standalone_fini_sysctl (struct net *net) {struct nf_conntrack_net *cnet =nf_ct_pernet (net); -struct ctl_table *table; +const struct ctl_table *table; table = cnet->sysctl_header->ctl_table_arg;unregister_net_sysctl_table (cnet->sysctl_header);
net/netfilter/nf_log.c +1 -1
@@ -509,7 +509,7 @@ err_alloc:static void netfilter_log_sysctl_exit (struct net *net) { -struct ctl_table *table; +const struct ctl_table *table; table = net->nf.nf_log_dir_header->ctl_table_arg;unregister_net_sysctl_table (net->nf.nf_log_dir_header);
net/sctp/sysctl.c +1 -1
@@ -630,7 +630,7 @@ int sctp_sysctl_net_register(struct netvoid sctp_sysctl_net_unregister (struct net *net) { -struct ctl_table *table; +const struct ctl_table *table; table = net->sctp.sysctl_header->ctl_table_arg;unregister_net_sysctl_table (net->sctp.sysctl_header);
net/smc/smc_sysctl.c +1 -1
@@ -109,7 +109,7 @@ err_alloc: void __net_exit smc_sysctl_net_exit(struct net *net) { -struct ctl_table *table; +const struct ctl_table *table; table = net->smc.smc_hdr->ctl_table_arg;unregister_net_sysctl_table (net->smc.smc_hdr);
net/unix/sysctl_net_unix.c +1 -1
@@ -52,7 +52,7 @@ err_alloc:void unix_sysctl_unregister (struct net *net) { -struct ctl_table *table; +const struct ctl_table *table; table = net->unx.ctl->ctl_table_arg;unregister_net_sysctl_table (net->unx.ctl);
net/xfrm/xfrm_sysctl.c +1 -1
@@ -76,7 +76,7 @@ out_kmemdup: void __net_exit xfrm_sysctl_fini(struct net *net) { -struct ctl_table *table; +const struct ctl_table *table; table = net->xfrm.sysctl_hdr->ctl_table_arg;unregister_net_sysctl_table (net->xfrm.sysctl_hdr);
Greg Kroah-Hartman
maintainerPATCH 243/261 sctp: avoid auth_enable sysctl UAF during netns teardown
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
[ Upstream commit f8d5e7846025f4ab15a461235f8ebae9094a361a ]
proc_sctp_do_auth() updates the SCTP control socket after changing net.sctp.auth_enable. The handler gets the per-net SCTP state from ctl->data, so an already opened sysctl file can still target a network namespace while that namespace is being torn down.
SCTP previously registered its per-net sysctls from sctp_defaults_init(), while the control socket is created later from sctp_ctrlsock_init(). This exposed a window during initialization where auth_enable was writable before net->sctp.ctl_sock existed, and a teardown window where auth_enable stayed writable after inet_ctl_sock_destroy() had released the control socket.
Move the per-net SCTP sysctl registration into sctp_ctrlsock_init() after sctp_ctl_sock_init() succeeds, and unregister the sysctl table before destroying the control socket in sctp_ctrlsock_exit(). If sysctl registration fails after the control socket was created, destroy the control socket in the same init path.
Make sctp_sysctl_net_unregister() tolerate a missing header and clear the saved pointer so init-error and exit paths can safely share the unregister helper.
--- net/sctp/protocol.c | 20 ++++++++++++-------- net/sctp/sysctl.c | 9 +++++++-- 2 files changed, 19 insertions(+), 10 deletions(-)
net/sctp/protocol.c +12 -8
@@ -1394,10 +1394,6 @@ static int __net_init sctp_defaults_init net->sctp.l3mdev_accept =1 ;#endif - status =sctp_sysctl_net_register (net); -if (status) - goto err_sysctl_register; -/* Allocate and initialise sctp mibs. */ status =init_sctp_mibs (net);if (status)
@@ -1431,8 +1427,6 @@ err_init_proc:cleanup_sctp_mibs (net);#endif err_init_mibs: -sctp_sysctl_net_unregister (net); -err_sysctl_register:return status; }
@@ -1447,7 +1441,6 @@ static void __net_exit sctp_defaults_exi net->sctp.proc_net_sctp =NULL ;#endif cleanup_sctp_mibs (net); -sctp_sysctl_net_unregister (net); }static struct pernet_operations sctp_defaults_ops = {
@@ -1461,16 +1454,27 @@ static int __net_init sctp_ctrlsock_init/* Initialize the control inode/socket for handling OOTB packets. */ status =sctp_ctl_sock_init (net); -if (status) +if (status) {pr_err ("Failed to initialize the SCTP control sock\n" ); +return status; + } + + status =sctp_sysctl_net_register (net); +if (status) { +inet_ctl_sock_destroy (net->sctp.ctl_sock); + net->sctp.ctl_sock =NULL ; + }return status; } static void __net_exit sctp_ctrlsock_exit(struct net *net) { +sctp_sysctl_net_unregister (net); +/* Free the control endpoint. */ inet_ctl_sock_destroy (net->sctp.ctl_sock); + net->sctp.ctl_sock =NULL ; }static struct pernet_operations sctp_ctrlsock_ops = {
net/sctp/sysctl.c +7 -2
@@ -630,11 +630,16 @@ int sctp_sysctl_net_register(struct netvoid sctp_sysctl_net_unregister (struct net *net) { +struct ctl_table_header *header = net->sctp.sysctl_header;const struct ctl_table *table; - table = net->sctp.sysctl_header->ctl_table_arg; -unregister_net_sysctl_table (net->sctp.sysctl_header); +if (!header) +return ; + + table = header->ctl_table_arg; +unregister_net_sysctl_table (header);kfree (table); + net->sctp.sysctl_header =NULL ; }static struct ctl_table_header *sctp_sysctl_header;
Greg Kroah-Hartman
maintainerPATCH 244/261 can: use skb hash instead of private variable in headroom
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit d4fb6514ff8ed6912a71294e6b66a5d59ee88007 upstream.
The can_skb_priv::skbcnt variable is used to identify CAN skbs in the RX path analogue to the skb->hash.
As the skb hash is not filled in CAN skbs move the private skbcnt value to skb->hash and set skb->sw_hash accordingly. The skb->hash is a value used for RPS to identify skbs. Use it as intended.
--- drivers/net/can/dev/skb.c | 2 -- include/linux/can/core.h | 1 + include/linux/can/skb.h | 2 -- net/can/af_can.c | 14 +++++++++++--- net/can/bcm.c | 2 -- net/can/isotp.c | 3 --- net/can/j1939/socket.c | 1 - net/can/j1939/transport.c | 2 -- net/can/raw.c | 7 +++---- 9 files changed, 15 insertions(+), 19 deletions(-)
drivers/net/can/dev/skb.c +0 -2
@@ -202,7 +202,6 @@ static void init_can_skb_reserve(structskb_reset_transport_header (skb);can_skb_reserve (skb); -can_skb_prv (skb)->skbcnt =0 ; }struct sk_buff *alloc_can_skb (struct net_device *dev,struct can_frame **cf)
@@ -312,7 +311,6 @@ static bool can_skb_headroom_valid(strucif (skb->ip_summed ==CHECKSUM_NONE ) {/* init headroom */ can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; skb->ip_summed =CHECKSUM_UNNECESSARY ;
include/linux/can/core.h +1 -0
@@ -58,6 +58,7 @@ extern void can_rx_unregister(struct net void *data);extern int can_send (struct sk_buff *skb,int loop); +void can_set_skb_uid (struct sk_buff *skb);void can_sock_destruct (struct sock *sk);#endif /* !_CAN_CORE_H */
include/linux/can/skb.h +0 -2
@@ -49,13 +49,11 @@ bool can_dropped_invalid_skb(struct net_/** * struct can_skb_priv - private additional data inside CAN sk_buffs * @ifindex: ifindex of the first interface the CAN frame appeared on -* @skbcnt: atomic counter to have an unique id together with skb pointer * @frame_len: length of CAN frame in data link layer * @cf: align to the following CAN frame at skb->data */ struct can_skb_priv {int ifindex; -int skbcnt;unsigned int frame_len;struct can_frame cf[]; };
net/can/af_can.c +11 -3
@@ -639,6 +639,16 @@ static int can_rcv_filter(struct can_devreturn matches; } +void can_set_skb_uid (struct sk_buff *skb) +{ +/* create non-zero unique skb identifier together with *skb */ +while (!(skb->hash)) + skb->hash =atomic_inc_return (&skbcounter); + + skb->sw_hash =1 ; +} +EXPORT_SYMBOL (can_set_skb_uid); +static void can_receive (struct sk_buff *skb,struct net_device *dev) {struct can_dev_rcv_lists *dev_rcv_lists;
@@ -650,9 +660,7 @@ static void can_receive(struct sk_buff *atomic_long_inc (&pkg_stats->rx_frames);atomic_long_inc (&pkg_stats->rx_frames_delta); -/* create non-zero unique skb identifier together with *skb */ -while (!(can_skb_prv (skb)->skbcnt)) -can_skb_prv (skb)->skbcnt =atomic_inc_return (&skbcounter); +can_set_skb_uid (skb);rcu_read_lock ();
net/can/bcm.c +0 -2
@@ -338,7 +338,6 @@ static void bcm_can_tx(struct bcm_op *opcan_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ;skb_put_data (skb, cf, op->cfsiz);
@@ -1582,7 +1581,6 @@ static int bcm_tx_send(struct msghdr *ms }can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; skb->dev = dev;can_skb_set_owner (skb, sk); err =can_send (skb,1 );/* send with loopback */
net/can/isotp.c +0 -3
@@ -231,7 +231,6 @@ static int isotp_send_fc(struct sock *skcan_skb_reserve (nskb);can_skb_prv (nskb)->ifindex = dev->ifindex; -can_skb_prv (nskb)->skbcnt =0 ; nskb->dev = dev;can_skb_set_owner (nskb, sk);
@@ -796,7 +795,6 @@ static void isotp_send_cframe(struct isocan_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; cf = (struct canfd_frame *)skb->data;skb_put_zero (skb, so->ll.mtu);
@@ -1102,7 +1100,6 @@ static int isotp_sendmsg(struct socket *can_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; so->tx.len = size; so->tx.idx =0 ;
net/can/j1939/socket.c +0 -1
@@ -889,7 +889,6 @@ static struct sk_buff *j1939_sk_alloc_skcan_skb_reserve (skb);can_skb_prv (skb)->ifindex = ndev->ifindex; -can_skb_prv (skb)->skbcnt =0 ;skb_reserve (skb, offsetof(struct can_frame , data)); ret =memcpy_from_msg (skb_put (skb, size), msg, size);
net/can/j1939/transport.c +0 -2
@@ -613,7 +613,6 @@ sk_buff *j1939_tp_tx_dat_new(struct j193 skb->dev = priv->ndev;can_skb_reserve (skb);can_skb_prv (skb)->ifindex = priv->ndev->ifindex; -can_skb_prv (skb)->skbcnt =0 ;/* reserve CAN header */ skb_reserve (skb, offsetof(struct can_frame , data));
@@ -1553,7 +1552,6 @@ j1939_session *j1939_session_fresh_new(s skb->dev = priv->ndev;can_skb_reserve (skb);can_skb_prv (skb)->ifindex = priv->ndev->ifindex; -can_skb_prv (skb)->skbcnt =0 ; skcb =j1939_skb_to_cb (skb);memcpy (skcb, rel_skcb,sizeof (*skcb));
net/can/raw.c +3 -4
@@ -75,8 +75,8 @@ MODULE_ALIAS("can-proto-1");*/ struct uniqframe { -int skbcnt;const struct sk_buff *skb; +u32 hash;unsigned int join_rx_count; };
@@ -140,7 +140,7 @@ static void raw_rcv(struct sk_buff *oskb/* eliminate multiple filter matches for the same skb */ if (this_cpu_ptr (ro->uniq)->skb == oskb && -this_cpu_ptr (ro->uniq)->skbcnt ==can_skb_prv (oskb)->skbcnt) { +this_cpu_ptr (ro->uniq)->hash == oskb->hash) {if (!ro->join_filters)return ;
@@ -150,7 +150,7 @@ static void raw_rcv(struct sk_buff *oskbreturn ; }else {this_cpu_ptr (ro->uniq)->skb = oskb; -this_cpu_ptr (ro->uniq)->skbcnt =can_skb_prv (oskb)->skbcnt; +this_cpu_ptr (ro->uniq)->hash = oskb->hash;this_cpu_ptr (ro->uniq)->join_rx_count =1 ;/* drop first frame to check all enabled filters? */ if (ro->join_filters && ro->count >1 )
@@ -871,7 +871,6 @@ static int raw_sendmsg(struct socket *socan_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; -can_skb_prv (skb)->skbcnt =0 ;/* fill the skb before testing for valid CAN frames */ err =memcpy_from_msg (skb_put (skb, size), msg, size);
Greg Kroah-Hartman
maintainerPATCH 245/261 can: isotp: fix timer drain order, wakeup handling and tx_gen ordering
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 050f010f920da17c1044a4f174766ad553e770b6 upstream.
This patch is a follow-up to commit cf070fe33bfb ("can: isotp: serialize TX state transitions under so->rx_lock") which addresses following sashiko-bot findings:
- isotp_sendmsg(): drain so->txfrtimer first so a stale callback can't re-arm echotimer after the claim
- isotp_release(): wake so->wait after forcing ISOTP_SHUTDOWN so a sleeping sendmsg() claim isn't stranded
- isotp_sendmsg(): have both wait_event_interruptible() calls in isotp_sendmsg() also wake on ISOTP_SHUTDOWN and do not return claim to IDLE to avoid corrupting a concurrent isotp_release() process.
- isotp_sendmsg(): handle potential claim of a new transfer when the wait_event_interruptible() call returns in CAN_ISOTP_WAIT_TX_DONE mode. Don't touch timers and states of the new transfer if a new thread incremented so->tx_gen before getting the lock at err_event_drop.
- isotp_sendmsg(): handle a stuck can_send() and omit timer and state changes if a new transfer was claimed. wait_tx_done() returns the error recorded in so->tx_result[], tagged with the caller's own generation.
- isotp_tx_timeout(): on a claimed timeout, record the ECOMM error for the timed-out transfer's own generation in so->tx_result[]; sk->sk_err is raised unconditionally, same as every other error path here.
- isotp_tx_gen_done()/isotp_tx_timeout(): always read tx.state (acquire) before tx_gen - the reverse order let a weakly ordered CPU pair a fresh tx.state with a stale tx_gen/tx_result slot.
- isotp_sendmsg(): wait_tx_done: drain sk_err via sock_error() once we have read the result from so->tx_result[], so an already-reported error doesn't stay latched for a later poll()/SO_ERROR.
Also align the remaining lock-free so->tx.state/rx.state/cfecho accesses and use skb->hash as unique loopback echo frame indicator.
--- net/can/isotp.c | 317 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 230 insertions(+), 87 deletions(-)
net/can/isotp.c +230 -87
@@ -125,6 +125,15 @@ MODULE_PARM_DESC(max_pdu_size, "maximum#define ISOTP_FC_TIMEOUT 1/* 1 sec */ #define ISOTP_ECHO_TIMEOUT 2/* 2 secs */ +/* so->tx_result[so->tx_gen % ISOTP_TX_RESULT_SLOTS] holds the packed value +* (err << ISOTP_TX_RESULT_GEN_BITS | gen) for each tx generation slot, so it +* can be handled with a single READ_ONCE()/WRITE_ONCE() access. +*/ +#define ISOTP_TX_RESULT_SLOTS 4 +#define ISOTP_TX_RESULT_GEN_BITS 24 +#define ISOTP_TX_RESULT_GEN_MASK ((1U << ISOTP_TX_RESULT_GEN_BITS) - 1) +#define ISOTP_TX_RESULT_ERR_MASK 0xFF +enum {ISOTP_IDLE =0 ,ISOTP_WAIT_FIRST_FC ,
@@ -163,7 +172,8 @@ struct isotp_sock {u32 force_tx_stmin;u32 force_rx_stmin;u32 cfecho;/* consecutive frame echo tag */ -u32 tx_gen;/* generation, bumped per new tx transfer */ +u32 tx_gen;/* transfer generation, increased per new tx transfer */ +u32 tx_result[ISOTP_TX_RESULT_SLOTS ];/* per-generation result slots */ struct tpcon rx, tx;struct list_head notifier;wait_queue_head_t wait;
@@ -174,6 +184,65 @@ static LIST_HEAD(isotp_notifier_list);static DEFINE_SPINLOCK (isotp_notifier_lock);static struct isotp_sock *isotp_busy_notifier; +/* increase (24 bit) tx generation value */ +static u32 isotp_inc_tx_gen (u32 gen) +{ +return (gen +1 ) &ISOTP_TX_RESULT_GEN_MASK ; +} + +/* store 8 bit error and 24 bit tx generation values in packed u32 element */ +static u32 isotp_pack_tx_result (u32 gen,int err) +{ +return gen | ((u32 )err <<ISOTP_TX_RESULT_GEN_BITS ); +} + +/* get the 24 bit tx generation value from the tx result */ +static u32 isotp_get_tx_gen (u32 gen_err) +{ +return gen_err &ISOTP_TX_RESULT_GEN_MASK ; +} + +/* get the 8 bit error value from the tx result */ +static u32 isotp_get_tx_err (u32 gen_err) +{ +return (gen_err >>ISOTP_TX_RESULT_GEN_BITS ) &ISOTP_TX_RESULT_ERR_MASK ; +} + +/* store transfer result in per-generation%4 so->tx_result[] slot */ +static void isotp_set_tx_result (struct isotp_sock *so,u32 gen,int err) +{ +WRITE_ONCE (so->tx_result[gen %ISOTP_TX_RESULT_SLOTS ], +isotp_pack_tx_result (gen, err)); +} + +/* fetch the result recorded for 'gen', as a (negative) errno (0 for success) */ +static int isotp_get_tx_result (struct isotp_sock *so,u32 gen) +{ +u32 result =READ_ONCE (so->tx_result[gen %ISOTP_TX_RESULT_SLOTS ]); + +if (isotp_get_tx_gen (result) != gen) { +pr_notice_once ("can-isotp: tx_result[] slot reused before read\n" ); + +/* report failure rather than risk a false success */ +return -ECOMM ; + } + +return -(isotp_get_tx_err (result)); +} + +/* true if done, shut down or superseded ('gen' is no longer the active +* transfer). Reads tx.state first (acquire) so tx_gen/tx_result reads +* below see at least what that state write published (common sequence). +*/ +static bool isotp_tx_gen_done (struct isotp_sock *so,u32 gen) +{ +/* read tx.state first for the common sequence */ +u32 state =smp_load_acquire (&so->tx.state); + +return state ==ISOTP_IDLE || state ==ISOTP_SHUTDOWN || +READ_ONCE (so->tx_gen) != gen; +} +static inline struct isotp_sock *isotp_sk (const struct sock *sk) {return (struct isotp_sock *)sk;
@@ -196,7 +265,7 @@ static enum hrtimer_restart isotp_rx_tim rxtimer);struct sock *sk = &so->sk; -if (so->rx.state ==ISOTP_WAIT_DATA ) { +if (READ_ONCE (so->rx.state) ==ISOTP_WAIT_DATA ) {/* we did not get new data frames in time */ /* report 'connection timed out' */
@@ -205,7 +274,7 @@ static enum hrtimer_restart isotp_rx_timsk_error_report (sk);/* reset rx state */ - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE ); }return HRTIMER_NORESTART ;
@@ -362,20 +431,19 @@ static void isotp_send_cframe(struct isostatic int isotp_rcv_fc (struct isotp_sock *so,struct canfd_frame *cf,int ae) {struct sock *sk = &so->sk; +int tx_err =EBADMSG ;/* default for unknown FC status */ -if (so->tx.state !=ISOTP_WAIT_FC && - so->tx.state !=ISOTP_WAIT_FIRST_FC ) +if (READ_ONCE (so->tx.state) !=ISOTP_WAIT_FC && +READ_ONCE (so->tx.state) !=ISOTP_WAIT_FIRST_FC )return 0 ;hrtimer_cancel (&so->txtimer);/* isotp_tx_timeout() may have given up on this job while -* hrtimer_cancel() above waited for it to finish; so->rx_lock -* (held by our caller isotp_rcv()) rules out a concurrent claim, -* so a plain recheck is enough here. +* hrtimer_cancel() above waited for it to finish => recheck */ -if (so->tx.state !=ISOTP_WAIT_FC && - so->tx.state !=ISOTP_WAIT_FIRST_FC ) +if (READ_ONCE (so->tx.state) !=ISOTP_WAIT_FC && +READ_ONCE (so->tx.state) !=ISOTP_WAIT_FIRST_FC )return 1 ; if ((cf->len < ae + FC_CONTENT_SZ) ||
@@ -386,13 +454,15 @@ static int isotp_rcv_fc(struct isotp_socif (!sock_flag (sk,SOCK_DEAD ))sk_error_report (sk); - so->tx.state =ISOTP_IDLE ; +isotp_set_tx_result (so, so->tx_gen,EBADMSG ); +/* set to IDLE after publishing tx_result */ +smp_store_release (&so->tx.state,ISOTP_IDLE );wake_up_interruptible (&so->wait);return 1 ; }/* get communication parameters only from the first FC frame */ -if (so->tx.state ==ISOTP_WAIT_FIRST_FC ) { +if (READ_ONCE (so->tx.state) ==ISOTP_WAIT_FIRST_FC ) { so->txfc.bs = cf->data[ae +1 ]; so->txfc.stmin = cf->data[ae +2 ];
@@ -415,13 +485,13 @@ static int isotp_rcv_fc(struct isotp_soc so->tx_gap =ktime_add_ns (so->tx_gap, (so->txfc.stmin -0xF0 ) *100000 ); - so->tx.state =ISOTP_WAIT_FC ; +WRITE_ONCE (so->tx.state,ISOTP_WAIT_FC ); }switch (cf->data[ae] &0x0F ) {case ISOTP_FC_CTS : so->tx.bs =0 ; - so->tx.state =ISOTP_SENDING ; +WRITE_ONCE (so->tx.state,ISOTP_SENDING );/* send CF frame and enable echo timeout handling */ hrtimer_start (&so->echotimer,ktime_set (ISOTP_ECHO_TIMEOUT ,0 ),HRTIMER_MODE_REL_SOFT );
@@ -436,14 +506,19 @@ static int isotp_rcv_fc(struct isotp_soccase ISOTP_FC_OVFLW :/* overflow on receiver side - report 'message too long' */ - sk->sk_err =EMSGSIZE ; -if (!sock_flag (sk,SOCK_DEAD )) -sk_error_report (sk); + tx_err =EMSGSIZE ; fallthrough;default : -/* stop this tx job */ - so->tx.state =ISOTP_IDLE ; +/* reserved/unknown flow status (tx_err defaults to EBADMSG) */ + + sk->sk_err = tx_err; +if (!sock_flag (sk,SOCK_DEAD )) +sk_error_report (sk); + +isotp_set_tx_result (so, so->tx_gen, tx_err); +/* set to IDLE after publishing tx_result */ +smp_store_release (&so->tx.state,ISOTP_IDLE );wake_up_interruptible (&so->wait); }return 0 ;
@@ -456,7 +531,7 @@ static int isotp_rcv_sf(struct sock *sk,struct sk_buff *nskb;hrtimer_cancel (&so->rxtimer); - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE );if (!len || len > cf->len - pcilen)return 1 ;
@@ -490,7 +565,7 @@ static int isotp_rcv_ff(struct sock *sk,int ff_pci_sz;hrtimer_cancel (&so->rxtimer); - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE );/* get the used sender LL_DL from the (first) CAN frame data length */ so->rx.ll_dl =padlen (cf->len);
@@ -544,7 +619,7 @@ static int isotp_rcv_ff(struct sock *sk,/* initial setup for this pdu reception */ so->rx.sn =1 ; - so->rx.state =ISOTP_WAIT_DATA ; +WRITE_ONCE (so->rx.state,ISOTP_WAIT_DATA );/* no creation of flow control frames */ if (so->opt.flags &CAN_ISOTP_LISTEN_MODE )
@@ -562,7 +637,7 @@ static int isotp_rcv_cf(struct sock *sk,struct sk_buff *nskb;int i; -if (so->rx.state !=ISOTP_WAIT_DATA ) +if (READ_ONCE (so->rx.state) !=ISOTP_WAIT_DATA )return 0 ;/* drop if timestamp gap is less than force_rx_stmin nano secs */
@@ -577,11 +652,9 @@ static int isotp_rcv_cf(struct sock *sk,hrtimer_cancel (&so->rxtimer);/* isotp_rx_timer_handler() may have raced us for so->rx.state -* while hrtimer_cancel() above waited for it to finish, already -* reporting ETIMEDOUT and resetting the reception; don't process -* this CF into a reassembly that has already been given up on. +* while hrtimer_cancel() above waited for it to finish => recheck */ -if (so->rx.state !=ISOTP_WAIT_DATA ) +if (READ_ONCE (so->rx.state) !=ISOTP_WAIT_DATA )return 1 ;/* CFs are never longer than the FF */
@@ -602,7 +675,7 @@ static int isotp_rcv_cf(struct sock *sk,sk_error_report (sk);/* reset rx state */ - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE );return 1 ; } so->rx.sn++;
@@ -616,7 +689,7 @@ static int isotp_rcv_cf(struct sock *sk,if (so->rx.idx >= so->rx.len) {/* we are done */ - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE );if ((so->opt.flags &ISOTP_CHECK_PADDING ) &&check_pad (so, cf, i +1 , so->opt.rxpad_content)) {
@@ -687,8 +760,10 @@ static void isotp_rcv(struct sk_buff *skif (so->opt.flags &CAN_ISOTP_HALF_DUPLEX ) {/* check rx/tx path half duplex expectations */ -if ((so->tx.state !=ISOTP_IDLE && n_pci_type !=N_PCI_FC ) || - (so->rx.state !=ISOTP_IDLE && n_pci_type ==N_PCI_FC )) +if ((READ_ONCE (so->tx.state) !=ISOTP_IDLE && + n_pci_type !=N_PCI_FC ) || + (READ_ONCE (so->rx.state) !=ISOTP_IDLE && + n_pci_type ==N_PCI_FC )) goto out_unlock; }
@@ -782,6 +857,7 @@ static void isotp_send_cframe(struct isostruct canfd_frame *cf;int can_send_ret;int ae = (so->opt.flags &CAN_ISOTP_EXTEND_ADDR ) ?1 :0 ; +u32 old_cfecho; dev =dev_get_by_index (sock_net (sk), so->ifindex);if (!dev)
@@ -796,6 +872,9 @@ static void isotp_send_cframe(struct isocan_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; +/* set uid in tx skb to identify CF echo frames */ +can_set_skb_uid (skb); + cf = (struct canfd_frame *)skb->data;skb_put_zero (skb, so->ll.mtu);
@@ -812,12 +891,15 @@ static void isotp_send_cframe(struct iso skb->dev = dev;can_skb_set_owner (skb, sk); -/* cfecho should have been zero'ed by init/isotp_rcv_echo() */ -if (so->cfecho) -pr_notice_once ("can-isotp: cfecho is %08X != 0\n" , so->cfecho); +/* zero'ed by init/isotp_rcv_echo(); reached lock-free via +* isotp_txfr_timer_handler() too, so use READ_ONCE()/WRITE_ONCE() +*/ + old_cfecho =READ_ONCE (so->cfecho); +if (old_cfecho) +pr_notice_once ("can-isotp: cfecho is %08X != 0\n" , old_cfecho);/* set consecutive frame echo tag */ - so->cfecho = *(u32 *)cf->data; +WRITE_ONCE (so->cfecho, skb->hash);/* send frame with local echo enabled */ can_send_ret =can_send (skb,1 );
@@ -869,7 +951,6 @@ static void isotp_rcv_echo(struct sk_buf {struct sock *sk = (struct sock *)data;struct isotp_sock *so =isotp_sk (sk); -struct canfd_frame *cf = (struct canfd_frame *)skb->data;/* only handle my own local echo CF/SF skb's (no FF!) */ if (skb->sk != sk)
@@ -881,32 +962,35 @@ static void isotp_rcv_echo(struct sk_bufspin_lock (&so->rx_lock);/* so->cfecho may since belong to a new transfer; recheck under lock */ -if (so->cfecho != *(u32 *)cf->data) +if (READ_ONCE (so->cfecho) != skb->hash) goto out_unlock;/* cancel local echo timeout */ hrtimer_cancel (&so->echotimer);/* local echo skb with consecutive frame has been consumed */ - so->cfecho =0 ; +WRITE_ONCE (so->cfecho,0 );/* claiming a transfer also takes so->rx_lock, so a plain recheck * is enough: so->tx.state can't have flipped to ISOTP_SENDING for * a new claim while we're still in here */ -if (so->tx.state !=ISOTP_SENDING ) +if (READ_ONCE (so->tx.state) !=ISOTP_SENDING ) goto out_unlock;if (so->tx.idx >= so->tx.len) {/* we are done */ - so->tx.state =ISOTP_IDLE ; + +isotp_set_tx_result (so, so->tx_gen,0 ); +/* set to IDLE after publishing tx_result */ +smp_store_release (&so->tx.state,ISOTP_IDLE );wake_up_interruptible (&so->wait); goto out_unlock; }if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {/* stop and wait for FC with timeout */ - so->tx.state =ISOTP_WAIT_FC ; +WRITE_ONCE (so->tx.state,ISOTP_WAIT_FC );hrtimer_start (&so->txtimer,ktime_set (ISOTP_FC_TIMEOUT ,0 ),HRTIMER_MODE_REL_SOFT ); goto out_unlock;
@@ -928,16 +1012,20 @@ out_unlock:spin_unlock (&so->rx_lock); } -/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get -* cancelled under so->rx_lock elsewhere, so this must stay lock-free to -* avoid deadlocking with that; uses so->tx_gen instead to avoid tainting -* a new transfer with an error from the one that just timed out. +/* isotp_tx_timeout: we did not get any flow control or echo frame in time +* +* Shared by so->txtimer's and so->echotimer's callbacks. Both timers get +* cancelled under so->rx_lock elsewhere, so this must stay lock-free. +* +* tx.state is acquired before tx_gen. Common sequence in isotp_tx_gen_done(). +* cmpxchg() only orders itself, not the two preceding loads. */ static enum hrtimer_restart isotp_tx_timeout (struct isotp_sock *so) {struct sock *sk = &so->sk; +/* read tx.state first for the common sequence */ +u32 old_state =smp_load_acquire (&so->tx.state);u32 gen =READ_ONCE (so->tx_gen); -u32 old_state =READ_ONCE (so->tx.state);/* don't handle timeouts in IDLE or SHUTDOWN state */ if (old_state ==ISOTP_IDLE || old_state ==ISOTP_SHUTDOWN )
@@ -947,14 +1035,14 @@ static enum hrtimer_restart isotp_tx_timif (cmpxchg (&so->tx.state, old_state,ISOTP_IDLE ) != old_state)return HRTIMER_NORESTART ; -/* we did not get any flow control or echo frame in time */ +/* detected timeout: report 'communication error on send' */ -if (READ_ONCE (so->tx_gen) == gen) { -/* report 'communication error on send' */ - sk->sk_err =ECOMM ; -if (!sock_flag (sk,SOCK_DEAD )) -sk_error_report (sk); - } +/* a stale read of this slot by a waiter still falls back to ECOMM */ +isotp_set_tx_result (so, gen,ECOMM ); + + sk->sk_err =ECOMM ; +if (!sock_flag (sk,SOCK_DEAD )) +sk_error_report (sk);wake_up_interruptible (&so->wait);
@@ -989,7 +1077,7 @@ static enum hrtimer_restart isotp_txfr_t HRTIMER_MODE_REL_SOFT);/* cfecho should be consumed by isotp_rcv_echo() here */ -if (so->tx.state ==ISOTP_SENDING && !so->cfecho) +if (READ_ONCE (so->tx.state) ==ISOTP_SENDING && !READ_ONCE (so->cfecho))isotp_send_cframe (so);return HRTIMER_NORESTART ;
@@ -1007,10 +1095,12 @@ static int isotp_sendmsg(struct socket *s64 hrtimer_sec =ISOTP_ECHO_TIMEOUT ;struct hrtimer *tx_hrt = &so->echotimer;u32 new_state =ISOTP_SENDING ; +u32 my_gen; +u32 old_cfecho;int off;int err; -if (!so->bound || so->tx.state ==ISOTP_SHUTDOWN ) +if (!so->bound ||READ_ONCE (so->tx.state) ==ISOTP_SHUTDOWN )return -EADDRNOTAVAIL ;/* claim the socket under so->rx_lock: this serializes the claim
@@ -1027,29 +1117,33 @@ static int isotp_sendmsg(struct socket *if (msg->msg_flags &MSG_DONTWAIT )return -EAGAIN ; -if (so->tx.state ==ISOTP_SHUTDOWN ) +if (READ_ONCE (so->tx.state) ==ISOTP_SHUTDOWN )return -EADDRNOTAVAIL ;/* wait for complete transmission of current pdu */ err =wait_event_interruptible (so->wait, - so->tx.state ==ISOTP_IDLE ); +READ_ONCE (so->tx.state) ==ISOTP_IDLE || +READ_ONCE (so->tx.state) ==ISOTP_SHUTDOWN );if (err)return err; } -/* new transfer: bump so->tx_gen and drain the old one's timers, -* still under the so->rx_lock we just claimed the socket with -*/ -WRITE_ONCE (so->tx.state,ISOTP_SENDING ); -WRITE_ONCE (so->tx_gen,READ_ONCE (so->tx_gen) +1 ); +/* txfrtimer's callback re-arms echotimer lock-free: drain it first */ +hrtimer_cancel (&so->txfrtimer);hrtimer_cancel (&so->txtimer);hrtimer_cancel (&so->echotimer); -hrtimer_cancel (&so->txfrtimer); - so->cfecho =0 ; + +/* new transfer: increment so->tx_gen and set tx.state after barrier */ + my_gen =isotp_inc_tx_gen (READ_ONCE (so->tx_gen)); +isotp_set_tx_result (so, my_gen,ECOMM );/* prevent stale slot matching */ +WRITE_ONCE (so->tx_gen, my_gen); +smp_wmb ();/* see smp_load_acquire() in isotp_tx_[timeout|gen_done] */ +WRITE_ONCE (so->tx.state,ISOTP_SENDING ); +WRITE_ONCE (so->cfecho,0 );spin_unlock_bh (&so->rx_lock);/* so->bound is only checked once above - a wakeup may have -* unbound/rebound the socket meanwhile, so re-validate it +* unbound/rebound the socket meanwhile => recheck */ if (!so->bound) { err = -EADDRNOTAVAIL ;
@@ -1101,6 +1195,9 @@ static int isotp_sendmsg(struct socket *can_skb_reserve (skb);can_skb_prv (skb)->ifindex = dev->ifindex; +/* set uid in tx skb to identify CF echo frames */ +can_set_skb_uid (skb); + so->tx.len = size; so->tx.idx =0 ;
@@ -1108,8 +1205,9 @@ static int isotp_sendmsg(struct socket *skb_put_zero (skb, so->ll.mtu);/* cfecho should have been zero'ed by init / former isotp_rcv_echo() */ -if (so->cfecho) -pr_notice_once ("can-isotp: uninit cfecho %08X\n" , so->cfecho); + old_cfecho =READ_ONCE (so->cfecho); +if (old_cfecho) +pr_notice_once ("can-isotp: uninit cfecho %08X\n" , old_cfecho);/* check for single frame transmission depending on TX_DL */ if (size <= so->tx.ll_dl -SF_PCI_SZ4 - ae - off) {
@@ -1137,7 +1235,7 @@ static int isotp_sendmsg(struct socket * cf->data[ae] |= size;/* set CF echo tag for isotp_rcv_echo() (SF-mode) */ - so->cfecho = *(u32 *)cf->data; +WRITE_ONCE (so->cfecho, skb->hash); }else {/* send first frame */
@@ -1154,7 +1252,7 @@ static int isotp_sendmsg(struct socket * so->txfc.bs =0 ;/* set CF echo tag for isotp_rcv_echo() (CF-mode) */ - so->cfecho = *(u32 *)cf->data; +WRITE_ONCE (so->cfecho, skb->hash); }else {/* standard flow control check */ new_state =ISOTP_WAIT_FIRST_FC ;
@@ -1164,12 +1262,12 @@ static int isotp_sendmsg(struct socket * tx_hrt = &so->txtimer;/* no CF echo tag for isotp_rcv_echo() (FF-mode) */ - so->cfecho =0 ; +WRITE_ONCE (so->cfecho,0 ); } }spin_lock_bh (&so->rx_lock); -if (so->tx.state ==ISOTP_SHUTDOWN ) { +if (READ_ONCE (so->tx.state) ==ISOTP_SHUTDOWN ) {/* isotp_release() has since taken over and already drained * our timers - don't send into a socket that's going away */
@@ -1180,7 +1278,7 @@ static int isotp_sendmsg(struct socket *return -EADDRNOTAVAIL ; }/* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */ - so->tx.state = new_state; +WRITE_ONCE (so->tx.state, new_state);hrtimer_start (tx_hrt,ktime_set (hrtimer_sec,0 ),HRTIMER_MODE_REL_SOFT );spin_unlock_bh (&so->rx_lock);
@@ -1197,20 +1295,49 @@ static int isotp_sendmsg(struct socket * __func__, ERR_PTR(err));spin_lock_bh (&so->rx_lock); + +/* new transfer already claimed by a concurrent completion, +* timeout or sendmsg() while we were stuck in can_send()? +*/ +if (READ_ONCE (so->tx_gen) != my_gen) { +/* don't touch timers and state of the new transfer */ +spin_unlock_bh (&so->rx_lock); +return err; + } +/* no transmission -> no timeout monitoring */ hrtimer_cancel (tx_hrt); goto err_out_drop_locked; }if (wait_tx_done) { -/* wait for complete transmission of current pdu */ - err =wait_event_interruptible (so->wait, so->tx.state ==ISOTP_IDLE ); +/* wake up for: +* - concurrent sendmsg() claiming a new transfer +* - complete transmission of current PDU +* - shutdown state change in isotp_release() +* isotp_tx_gen_done() uses common tx.state/tx_gen read sequence +*/ + err =wait_event_interruptible (so->wait, +isotp_tx_gen_done (so, my_gen));if (err) goto err_event_drop; - err =sock_error (sk); -if (err) -return err; +/* still our claim, but isotp_release() force-shut it down */ +if (smp_load_acquire (&so->tx.state) ==ISOTP_SHUTDOWN && +READ_ONCE (so->tx_gen) == my_gen) { + err = -EADDRNOTAVAIL ; + goto err_event_drop; + } + +/* own completion, or tx_gen moved on - either way this is +* what isotp_get_tx_result() recorded for my_gen +*/ + err =isotp_get_tx_result (so, my_gen); + +/* drain to avoid stale error for a later poll()/SO_ERROR */ +sock_error (sk); + +return err ? err : size; }return size;
@@ -1220,15 +1347,26 @@ err_out_drop:spin_lock_bh (&so->rx_lock); goto err_out_drop_locked; err_event_drop: -/* interrupted waiting on our own transfer - drain its timers */ +/* interrupted or shut down while waiting on our own transfer */ spin_lock_bh (&so->rx_lock); + +/* new transfer already started by concurrent sendmsg()? */ +if (READ_ONCE (so->tx_gen) != my_gen) { +/* don't touch timers and states of the new transfer */ +spin_unlock_bh (&so->rx_lock); +return err; + } +hrtimer_cancel (&so->txfrtimer);hrtimer_cancel (&so->txtimer);hrtimer_cancel (&so->echotimer); err_out_drop_locked:/* release the claim; so->rx_lock still held from above */ - so->cfecho =0 ; - so->tx.state =ISOTP_IDLE ; +WRITE_ONCE (so->cfecho,0 ); + +/* only claim to IDLE if isotp_release() has not taken over */ +if (READ_ONCE (so->tx.state) !=ISOTP_SHUTDOWN ) +WRITE_ONCE (so->tx.state,ISOTP_IDLE );spin_unlock_bh (&so->rx_lock);wake_up_interruptible (&so->wait);
@@ -1294,8 +1432,9 @@ static int isotp_release(struct socket */* best-effort: wait for a running pdu to finish, but don't block on * it forever - give up after the first signal */ -while (so->tx.state !=ISOTP_IDLE && -wait_event_interruptible (so->wait, so->tx.state ==ISOTP_IDLE ) ==0 ) +while (READ_ONCE (so->tx.state) !=ISOTP_IDLE && +wait_event_interruptible (so->wait, +READ_ONCE (so->tx.state) ==ISOTP_IDLE ) ==0 ) ;/* claim the socket under so->rx_lock like sendmsg() does, so its
@@ -1303,9 +1442,12 @@ static int isotp_release(struct socket ** unconditionally, even when a signal cut the wait above short */ spin_lock_bh (&so->rx_lock); - so->tx.state =ISOTP_SHUTDOWN ; +WRITE_ONCE (so->tx.state,ISOTP_SHUTDOWN );spin_unlock_bh (&so->rx_lock); - so->rx.state =ISOTP_IDLE ; +WRITE_ONCE (so->rx.state,ISOTP_IDLE ); + +/* forced SHUTDOWN may have skipped IDLE (gave up on a signal) */ +wake_up_interruptible (&so->wait);spin_lock (&isotp_notifier_lock);while (isotp_busy_notifier == so) {
@@ -1420,7 +1562,8 @@ static int isotp_bind(struct socket *soc* with so->bound in the same lock_sock() section above, so there is * no window in which a concurrent isotp_notify() could be missed. */ -if (so->tx.state !=ISOTP_IDLE || so->rx.state !=ISOTP_IDLE ) { +if (READ_ONCE (so->tx.state) !=ISOTP_IDLE || +READ_ONCE (so->rx.state) !=ISOTP_IDLE ) { err = -EAGAIN ; goto out; }
@@ -1454,7 +1597,7 @@ static int isotp_bind(struct socket *soc isotp_rcv, sk, "isotp", sk);/* no consecutive frame echo skb in flight */ - so->cfecho =0 ; +WRITE_ONCE (so->cfecho,0 );/* register for echo skb's */ can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id),
@@ -1820,7 +1963,7 @@ static __poll_t isotp_poll(struct file *poll_wait (file, &so->wait, wait);/* Check for false positives due to TX state */ -if ((mask &EPOLLWRNORM ) && (so->tx.state !=ISOTP_IDLE )) +if ((mask &EPOLLWRNORM ) && (READ_ONCE (so->tx.state) !=ISOTP_IDLE )) mask &= ~(EPOLLOUT |EPOLLWRNORM );return mask;
Greg Kroah-Hartman
maintainerPATCH 246/261 libceph: add doutc and *_client debug macros support
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiubo Li <[email protected]>
[ Upstream commit 5c5f0d2b5f92c47baf82b9b211e27edd7d195158 ]
This will help print the fsid and client's global_id in debug logs, and also print the function names.
[ idryomov: %lld -> %llu, leading space for doutc(), don't include
__func__ in pr_*() variants ]
Stable-dep-of: c3e64079d8b9 ("ceph: fix refcount leak in ceph_readdir()")
--- include/linux/ceph/ceph_debug.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
include/linux/ceph/ceph_debug.h +38 -0
@@ -19,12 +19,25 @@pr_debug ("%.*s %12.12s:%-4d : " fmt, \8 - (int )sizeof (KBUILD_MODNAME )," " , \ kbasename(__FILE__), __LINE__, ##__VA_ARGS__) +# define doutc (client, fmt, ...) \ + pr_debug("%.*s %12.12s:%-4d : [%pU %llu] " fmt, \ + 8 - (int)sizeof(KBUILD_MODNAME), " ", \ + kbasename(__FILE__), __LINE__, \ + &client->fsid, client->monc.auth->global_id, \ + ##__VA_ARGS__)# else /* faux printk call just to see any compiler warnings. */ # define dout (fmt, ...) do { \ if (0) \ printk(KERN_DEBUG fmt, ##__VA_ARGS__); \ } while (0) +# define doutc (client, fmt, ...) do { \ + if (0) \ + printk(KERN_DEBUG "[%pU %llu] " fmt, \ + &client->fsid, \ + client->monc.auth->global_id, \ + ##__VA_ARGS__); \ + } while (0)# endif #else
@@ -33,7 +46,32 @@* or, just wrap pr_debug */ # define dout (fmt, ...) pr_debug(" " fmt, ##__VA_ARGS__) +# define doutc (client, fmt, ...) \ + pr_debug(" [%pU %llu] %s: " fmt, &client->fsid, \ + client->monc.auth->global_id, __func__, ##__VA_ARGS__)#endif +#define pr_notice_client (client, fmt, ...) \ + pr_notice("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_info_client (client, fmt, ...) \ + pr_info("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_client (client, fmt, ...) \ + pr_warn("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_once_client (client, fmt, ...) \ + pr_warn_once("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_err_client (client, fmt, ...) \ + pr_err("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_warn_ratelimited_client (client, fmt, ...) \ + pr_warn_ratelimited("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#define pr_err_ratelimited_client (client, fmt, ...) \ + pr_err_ratelimited("[%pU %llu]: " fmt, &client->fsid, \ + client->monc.auth->global_id, ##__VA_ARGS__) +#endif
Greg Kroah-Hartman
maintainerPATCH 247/261 ceph: print cluster fsid and client global_id in all debug logs
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiubo Li <[email protected]>
[ Upstream commit 38d46409c4639a1d659ebfa70e27a8bed6b8ee1d ]
Multiple CephFS mounts on a host is increasingly common so disambiguating messages like this is necessary and will make it easier to debug issues.
At the same this will improve the debug logs to make them easier to troubleshooting issues, such as print the ino# instead only printing the memory addresses of the corresponding inodes and print the dentry names instead of the corresponding memory addresses for the dentry,etc.
Stable-dep-of: c3e64079d8b9 ("ceph: fix refcount leak in ceph_readdir()")
--- fs/ceph/acl.c | 6 fs/ceph/addr.c | 279 +++++++++++--------- fs/ceph/caps.c | 708 +++++++++++++++++++++++++++++---------------------- fs/ceph/crypto.c | 39 +- fs/ceph/debugfs.c | 6 fs/ceph/dir.c | 218 +++++++++------ fs/ceph/export.c | 39 +- fs/ceph/file.c | 245 ++++++++++------- fs/ceph/inode.c | 485 +++++++++++++++++++--------------- fs/ceph/ioctl.c | 13 fs/ceph/locks.c | 57 ++-- fs/ceph/mds_client.c | 554 ++++++++++++++++++++++----------------- fs/ceph/mdsmap.c | 24 - fs/ceph/metric.c | 5 fs/ceph/quota.c | 29 +- fs/ceph/snap.c | 174 ++++++------ fs/ceph/super.c | 70 ++--- fs/ceph/super.h | 6 fs/ceph/xattr.c | 96 ++++-- 19 files changed, 1744 insertions(+), 1309 deletions(-)
fs/ceph/acl.c +4 -2
@@ -15,6 +15,7 @@#include <linux/slab.h> #include "super.h" +#include "mds_client.h" static inline void ceph_set_cached_acl (struct inode *inode,int type,struct posix_acl *acl)
@@ -31,6 +32,7 @@ static inline void ceph_set_cached_acl(sstruct posix_acl *ceph_get_acl (struct inode *inode,int type,bool rcu) { +struct ceph_client *cl =ceph_inode_to_client (inode);int size;unsigned int retry_cnt =0 ;const char *name;
@@ -72,8 +74,8 @@ retry: }else if (size == -ENODATA || size ==0 ) { acl =NULL ; }else { -pr_err_ratelimited ("get acl %llx.%llx failed, err=%d\n" , -ceph_vinop (inode), size); +pr_err_ratelimited_client (cl,"%llx.%llx failed, err=%d\n" , +ceph_vinop (inode), size); acl =ERR_PTR (-EIO ); }
fs/ceph/addr.c +153 -126
757 lines not on this page · raw message
fs/ceph/caps.c +409 -299
1,716 lines not on this page · raw message
fs/ceph/crypto.c +26 -13
@@ -211,6 +211,7 @@ void ceph_fscrypt_as_ctx_to_req(struct cstatic struct inode *parse_longname (const struct inode *parent,const char *name,int *name_len) { +struct ceph_client *cl =ceph_inode_to_client (parent);struct inode *dir =NULL ;struct ceph_vino vino = { .snap =CEPH_NOSNAP };char *inode_number;
@@ -222,12 +223,12 @@ static struct inode *parse_longname(cons name++; name_end =strrchr (name,'_' );if (!name_end) { -dout ("Failed to parse long snapshot name: %s\n" , name); +doutc (cl,"failed to parse long snapshot name: %s\n" , name);return ERR_PTR (-EIO ); } *name_len = (name_end - name);if (*name_len <=0 ) { -pr_err ("Failed to parse long snapshot name\n" ); +pr_err_client (cl,"failed to parse long snapshot name\n" );return ERR_PTR (-EIO ); }
@@ -239,7 +240,7 @@ static struct inode *parse_longname(consreturn ERR_PTR (-ENOMEM ); ret =kstrtou64 (inode_number,10 , &vino.ino);if (ret) { -dout ("Failed to parse inode number: %s\n" , name); +doutc (cl,"failed to parse inode number: %s\n" , name); dir =ERR_PTR (ret); goto out; }
@@ -250,7 +251,7 @@ static struct inode *parse_longname(cons/* This can happen if we're not mounting cephfs on the root */ dir =ceph_get_inode (parent->i_sb, vino,NULL );if (IS_ERR (dir)) -dout ("Can't find inode %s (%s)\n" , inode_number, name); +doutc (cl,"can't find inode %s (%s)\n" , inode_number, name); } out:
@@ -261,6 +262,7 @@ out:int ceph_encode_encrypted_dname (struct inode *parent,struct qstr *d_name,char *buf) { +struct ceph_client *cl =ceph_inode_to_client (parent);struct inode *dir = parent;struct qstr iname;u32 len;
@@ -329,7 +331,7 @@ int ceph_encode_encrypted_dname(struct i/* base64 encode the encrypted name */ elen =ceph_base64_encode (cryptbuf, len, buf); -dout ("base64-encoded ciphertext name = %.*s\n" , elen, buf); +doutc (cl,"base64-encoded ciphertext name = %.*s\n" , elen, buf);/* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */ WARN_ON (elen >240 );
@@ -504,7 +506,10 @@ int ceph_fscrypt_decrypt_block_inplace(c struct page *page, unsigned int len, unsigned int offs, u64 lblk_num) { - dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num); + struct ceph_client *cl = ceph_inode_to_client(inode); + +doutc (cl,"%p %llx.%llx len %u offs %u blk %llu\n" , inode, +ceph_vinop (inode), len, offs, lblk_num);return fscrypt_decrypt_block_inplace (inode, page, len, offs, lblk_num); }
@@ -513,7 +518,10 @@ int ceph_fscrypt_encrypt_block_inplace(c unsigned int offs, u64 lblk_num, gfp_t gfp_flags) { - dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num); + struct ceph_client *cl = ceph_inode_to_client(inode); + +doutc (cl,"%p %llx.%llx len %u offs %u blk %llu\n" , inode, +ceph_vinop (inode), len, offs, lblk_num);return fscrypt_encrypt_block_inplace (inode, page, len, offs, lblk_num, gfp_flags); }
@@ -582,6 +590,7 @@ int ceph_fscrypt_decrypt_extents(struct u64 off, struct ceph_sparse_extent *map, u32 ext_cnt) { + struct ceph_client *cl = ceph_inode_to_client(inode);int i, ret =0 ;struct ceph_inode_info *ci =ceph_inode (inode);u64 objno, objoff;
@@ -589,7 +598,8 @@ int ceph_fscrypt_decrypt_extents(struct/* Nothing to do for empty array */ if (ext_cnt ==0 ) { -dout ("%s: empty array, ret 0\n" , __func__); +doutc (cl,"%p %llx.%llx empty array, ret 0\n" , inode, +ceph_vinop (inode));return 0 ; }
@@ -603,14 +613,17 @@ int ceph_fscrypt_decrypt_extents(structint fret;if ((ext->off | ext->len) & ~CEPH_FSCRYPT_BLOCK_MASK ) { -pr_warn ("%s: bad encrypted sparse extent idx %d off %llx len %llx\n" , - __func__, i, ext->off, ext->len); +pr_warn_client (cl, +"%p %llx.%llx bad encrypted sparse extent " +"idx %d off %llx len %llx\n" , + inode,ceph_vinop (inode), i, ext->off, + ext->len);return -EIO ; } fret =ceph_fscrypt_decrypt_pages (inode, &page[pgidx], off + pgsoff, ext->len); -dout ("%s: [%d] 0x%llx~0x%llx fret %d\n" , __func__, i, - ext->off, ext->len, fret); +doutc (cl,"%p %llx.%llx [%d] 0x%llx~0x%llx fret %d\n" , inode, +ceph_vinop (inode), i, ext->off, ext->len, fret);if (fret <0 ) {if (ret ==0 ) ret = fret;
@@ -618,7 +631,7 @@ int ceph_fscrypt_decrypt_extents(struct } ret = pgsoff + fret; } -dout ("%s: ret %d\n" , __func__, ret); +doutc (cl,"ret %d\n" , ret);return ret; }
fs/ceph/debugfs.c +4 -2
@@ -398,7 +398,7 @@ DEFINE_SIMPLE_ATTRIBUTE(congestion_kb_fovoid ceph_fs_debugfs_cleanup (struct ceph_fs_client *fsc) { -dout ("ceph_fs_debugfs_cleanup\n" ); +doutc (fsc->client,"begin\n" );debugfs_remove (fsc->debugfs_bdi);debugfs_remove (fsc->debugfs_congestion_kb);debugfs_remove (fsc->debugfs_mdsmap);
@@ -407,13 +407,14 @@ void ceph_fs_debugfs_cleanup(struct cephdebugfs_remove (fsc->debugfs_status);debugfs_remove (fsc->debugfs_mdsc);debugfs_remove_recursive (fsc->debugfs_metrics_dir); +doutc (fsc->client,"done\n" ); }void ceph_fs_debugfs_init (struct ceph_fs_client *fsc) {char name[100 ]; -dout ("ceph_fs_debugfs_init\n" ); +doutc (fsc->client,"begin\n" ); fsc->debugfs_congestion_kb = debugfs_create_file("writeback_congestion_kb", 0600,
@@ -469,6 +470,7 @@ void ceph_fs_debugfs_init(struct ceph_fs &metrics_size_fops);debugfs_create_file ("caps" ,0400 , fsc->debugfs_metrics_dir, fsc, &metrics_caps_fops); +doutc (fsc->client,"done\n" ); }
fs/ceph/dir.c +132 -86
354 lines not on this page · raw message
fs/ceph/export.c +22 -17
Lines of this file are not shown. View the raw message.
fs/ceph/file.c +141 -104
245 lines not on this page · raw message
fs/ceph/inode.c +267 -218
485 lines not on this page · raw message
fs/ceph/ioctl.c +9 -4
Lines of this file are not shown. View the raw message.
fs/ceph/locks.c +33 -24
57 lines not on this page · raw message
fs/ceph/mds_client.c +313 -241
554 lines not on this page · raw message
fs/ceph/mdsmap.c +12 -12
Lines of this file are not shown. View the raw message.
fs/ceph/metric.c +3 -2
Lines of this file are not shown. View the raw message.
fs/ceph/quota.c +18 -11
Lines of this file are not shown. View the raw message.
fs/ceph/snap.c +95 -79
174 lines not on this page · raw message
fs/ceph/super.c +40 -30
70 lines not on this page · raw message
fs/ceph/super.h +6 -0
Lines of this file are not shown. View the raw message.
fs/ceph/xattr.c +57 -39
96 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
Greg Kroah-Hartman
maintainerPATCH 248/261 ceph: fix refcount leak in ceph_readdir()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: WenTao Liang <[email protected]>
[ Upstream commit c3e64079d8b9663e3998d0caac9aba915b6b93ae ]
The ceph_readdir() function allocates a ceph_mds_request via ceph_mdsc_create_request() and stores it in dfi->last_readdir. In the directory entry processing loop, if the entry's offset is less than ctx->pos or if the inode pointer is unexpectedly NULL, the function returns -EIO without releasing the reference held by dfi->last_readdir, causing a refcount leak.
Fix this by adding ceph_mdsc_put_request(dfi->last_readdir) before returning on these error paths. Also set dfi->last_readdir to NULL for safety, matching the cleanup done at the normal exit.
--- fs/ceph/dir.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
fs/ceph/dir.c +6 -1
@@ -546,11 +546,16 @@ more:pr_warn_client (cl,"%p %llx.%llx rde->offset 0x%llx ctx->pos 0x%llx\n" , inode,ceph_vinop (inode), rde->offset, ctx->pos); +ceph_mdsc_put_request (dfi->last_readdir); + dfi->last_readdir =NULL ;return -EIO ; } -if (WARN_ON_ONCE (!rde->inode.in)) +if (WARN_ON_ONCE (!rde->inode.in)) { +ceph_mdsc_put_request (dfi->last_readdir); + dfi->last_readdir =NULL ;return -EIO ; + } ctx->pos = rde->offset; doutc(cl, "%p %llx.%llx (%d/%d) -> %llx '%.*s' %p\n", inode,
Greg Kroah-Hartman
maintainerPATCH 249/261 mptcp: pm: avoid code duplication to lookup endp
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geliang Tang <[email protected]>
[ Upstream commit 1d7fa6ceb91fddbe38cae3521d5d1075bce6a00e ]
The helper __lookup_addr() can be used in mptcp_pm_nl_get_local_id() and mptcp_pm_nl_is_backup() to simplify the code, and avoid code duplication.
Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
--- net/mptcp/pm_netlink.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-)
net/mptcp/pm_netlink.c +6 -14
@@ -1281,17 +1281,13 @@ int mptcp_pm_nl_get_local_id(struct mptc {struct mptcp_pm_addr_entry *entry;struct pm_nl_pernet *pernet; -int ret =-1 ; +int ret; pernet =pm_nl_get_pernet_from_msk (msk);rcu_read_lock (); -list_for_each_entry_rcu (entry, &pernet->local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, skc, entry->addr.port)) { - ret = entry->addr.id; -break ; - } - } + entry =__lookup_addr (pernet, skc); + ret = entry ? entry->addr.id :-1 ;rcu_read_unlock ();if (ret >=0 )return ret;
@@ -1318,15 +1314,11 @@ bool mptcp_pm_nl_is_backup(struct mptcp_ {struct pm_nl_pernet *pernet =pm_nl_get_pernet_from_msk (msk);struct mptcp_pm_addr_entry *entry; -bool backup = false; +bool backup;rcu_read_lock (); -list_for_each_entry_rcu (entry, &pernet->local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, skc, entry->addr.port)) { - backup = !!(entry->flags &MPTCP_PM_ADDR_FLAG_BACKUP ); -break ; - } - } + entry =__lookup_addr (pernet, skc); + backup = entry && !!(entry->flags &MPTCP_PM_ADDR_FLAG_BACKUP );rcu_read_unlock ();return backup;
Greg Kroah-Hartman
maintainerPATCH 250/261 mptcp: add mptcp_userspace_pm_lookup_addr helper
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geliang Tang <[email protected]>
[ Upstream commit e7b4083b90b7213902124d13fd1ed808360e32b1 ]
Like __lookup_addr() helper in pm_netlink.c, a new helper mptcp_userspace_pm_lookup_addr() is also defined in pm_userspace.c. It looks up the corresponding mptcp_pm_addr_entry address in userspace_pm_local_addr_list through the passed "addr" parameter and returns the found address entry.
This helper can be used in mptcp_userspace_pm_delete_local_addr(), mptcp_userspace_pm_set_flags(), mptcp_userspace_pm_get_local_id() and mptcp_userspace_pm_is_backup() to simplify the code.
Please note that with this change now list_for_each_entry() is used in mptcp_userspace_pm_append_new_local_addr(), not list_for_each_entry_safe(), but that's OK to do so because mptcp_userspace_pm_lookup_addr() only returns an entry from the list, the list hasn't been modified here.
Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
--- net/mptcp/pm_userspace.c | 71 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 35 deletions(-)
net/mptcp/pm_userspace.c +36 -35
@@ -25,6 +25,19 @@ void mptcp_free_local_addr_list(struct m } } +static struct mptcp_pm_addr_entry * +mptcp_userspace_pm_lookup_addr (struct mptcp_sock *msk, +const struct mptcp_addr_info *addr) +{ +struct mptcp_pm_addr_entry *entry; + +list_for_each_entry (entry, &msk->pm.userspace_pm_local_addr_list, list) { +if (mptcp_addresses_equal (&entry->addr, addr, false)) +return entry; + } +return NULL ; +} +static int mptcp_userspace_pm_append_new_local_addr (struct mptcp_sock *msk,struct mptcp_pm_addr_entry *entry,bool needs_id)
@@ -89,22 +102,20 @@ append_err:static int mptcp_userspace_pm_delete_local_addr (struct mptcp_sock *msk,struct mptcp_pm_addr_entry *addr) { -struct mptcp_pm_addr_entry *entry, *tmp;struct sock *sk = (struct sock *)msk; +struct mptcp_pm_addr_entry *entry; -list_for_each_entry_safe (entry, tmp, &msk->pm.userspace_pm_local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, &addr->addr, false)) { -/* TODO: a refcount is needed because the entry can -* be used multiple times (e.g. fullmesh mode). -*/ -list_del_rcu (&entry->list); -sock_kfree_s (sk, entry,sizeof (*entry)); - msk->pm.local_addr_used--; -return 0 ; - } - } - -return -EINVAL ; + entry =mptcp_userspace_pm_lookup_addr (msk, &addr->addr); +if (!entry) +return -EINVAL ; + +/* TODO: a refcount is needed because the entry can +* be used multiple times (e.g. fullmesh mode). +*/ +list_del_rcu (&entry->list); +sock_kfree_s (sk, entry,sizeof (*entry)); + msk->pm.local_addr_used--; +return 0 ; } static struct mptcp_pm_addr_entry *
@@ -139,17 +150,12 @@ int mptcp_userspace_pm_get_flags_and_ifiint mptcp_userspace_pm_get_local_id (struct mptcp_sock *msk,struct mptcp_addr_info *skc) { -struct mptcp_pm_addr_entry *entry =NULL , *e, new_entry; +struct mptcp_pm_addr_entry *entry =NULL , new_entry;__be16 msk_sport = ((struct inet_sock *)inet_sk ((struct sock *)msk))->inet_sport;spin_lock_bh (&msk->pm.lock); -list_for_each_entry (e, &msk->pm.userspace_pm_local_addr_list, list) { -if (mptcp_addresses_equal (&e->addr, skc, false)) { - entry = e; -break ; - } - } + entry =mptcp_userspace_pm_lookup_addr (msk, skc);spin_unlock_bh (&msk->pm.lock);if (entry)return entry->addr.id;
@@ -169,15 +175,11 @@ bool mptcp_userspace_pm_is_backup(struct struct mptcp_addr_info *skc) {struct mptcp_pm_addr_entry *entry; -bool backup = false; +bool backup;spin_lock_bh (&msk->pm.lock); -list_for_each_entry (entry, &msk->pm.userspace_pm_local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, skc, false)) { - backup = !!(entry->flags &MPTCP_PM_ADDR_FLAG_BACKUP ); -break ; - } - } + entry =mptcp_userspace_pm_lookup_addr (msk, skc); + backup = entry && !!(entry->flags &MPTCP_PM_ADDR_FLAG_BACKUP );spin_unlock_bh (&msk->pm.lock);return backup;
@@ -591,13 +593,12 @@ int mptcp_userspace_pm_set_flags(struct goto set_flags_err;spin_lock_bh (&msk->pm.lock); -list_for_each_entry (entry, &msk->pm.userspace_pm_local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, &loc->addr, false)) { -if (bkup) - entry->flags |=MPTCP_PM_ADDR_FLAG_BACKUP ; -else - entry->flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP ; - } + entry =mptcp_userspace_pm_lookup_addr (msk, &loc->addr); +if (entry) { +if (bkup) + entry->flags |=MPTCP_PM_ADDR_FLAG_BACKUP ; +else + entry->flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP ; }spin_unlock_bh (&msk->pm.lock);
Greg Kroah-Hartman
maintainerPATCH 251/261 mptcp: pm: use addr entry for get_local_id
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geliang Tang <[email protected]>
[ Upstream commit 7462fe22cc74321eb663768848976d42eba3ddbb ]
The following code in mptcp_userspace_pm_get_local_id() that assigns "skc" to "new_entry" is not allowed in BPF if we use the same code to implement the get_local_id() interface of a BFP path manager:
memset(&new_entry, 0, sizeof(struct mptcp_pm_addr_entry)); new_entry.addr = *skc; new_entry.addr.id = 0; new_entry.flags = MPTCP_PM_ADDR_FLAG_IMPLICIT;
To solve the issue, this patch moves this assignment to "new_entry" forward to mptcp_pm_get_local_id(), and then passing "new_entry" as a parameter to both mptcp_pm_nl_get_local_id() and mptcp_userspace_pm_get_local_id().
No behavioural changes intended.
Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
--- net/mptcp/pm.c | 9 ++++++--- net/mptcp/pm_netlink.c | 11 ++++------- net/mptcp/pm_userspace.c | 17 ++++++----------- net/mptcp/protocol.h | 6 ++++-- 4 files changed, 20 insertions(+), 23 deletions(-)
net/mptcp/pm.c +6 -3
@@ -428,7 +428,7 @@ out_unlock:int mptcp_pm_get_local_id (struct mptcp_sock *msk,struct sock_common *skc) { -struct mptcp_addr_info skc_local; +struct mptcp_pm_addr_entry skc_local = {0 };struct mptcp_addr_info msk_local;if (WARN_ON_ONCE (!msk))
@@ -438,10 +438,13 @@ int mptcp_pm_get_local_id(struct mptcp_s* addr */ mptcp_local_address ((struct sock_common *)msk, &msk_local); -mptcp_local_address ((struct sock_common *)skc, &skc_local); -if (mptcp_addresses_equal (&msk_local, &skc_local, false)) +mptcp_local_address ((struct sock_common *)skc, &skc_local.addr); +if (mptcp_addresses_equal (&msk_local, &skc_local.addr, false))return 0 ; + skc_local.addr.id =0 ; + skc_local.flags =MPTCP_PM_ADDR_FLAG_IMPLICIT ; +if (mptcp_pm_is_userspace (msk))return mptcp_userspace_pm_get_local_id (msk, &skc_local);return mptcp_pm_nl_get_local_id (msk, &skc_local);
net/mptcp/pm_netlink.c +4 -7
@@ -1277,7 +1277,8 @@ static int mptcp_pm_nl_create_listen_socreturn err; } -int mptcp_pm_nl_get_local_id (struct mptcp_sock *msk,struct mptcp_addr_info *skc) +int mptcp_pm_nl_get_local_id (struct mptcp_sock *msk, +struct mptcp_pm_addr_entry *skc) {struct mptcp_pm_addr_entry *entry;struct pm_nl_pernet *pernet;
@@ -1286,7 +1287,7 @@ int mptcp_pm_nl_get_local_id(struct mptc pernet =pm_nl_get_pernet_from_msk (msk);rcu_read_lock (); - entry =__lookup_addr (pernet, skc); + entry =__lookup_addr (pernet, &skc->addr); ret = entry ? entry->addr.id :-1 ;rcu_read_unlock ();if (ret >=0 )
@@ -1297,12 +1298,8 @@ int mptcp_pm_nl_get_local_id(struct mptcif (!entry)return -ENOMEM ; - entry->addr = *skc; - entry->addr.id =0 ; + *entry = *skc; entry->addr.port =0 ; - entry->ifindex =0 ; - entry->flags =MPTCP_PM_ADDR_FLAG_IMPLICIT ; - entry->lsk =NULL ; ret =mptcp_pm_nl_append_new_local_addr (pernet, entry, false);if (ret <0 )kfree (entry);
net/mptcp/pm_userspace.c +6 -11
@@ -148,27 +148,22 @@ int mptcp_userspace_pm_get_flags_and_ifi }int mptcp_userspace_pm_get_local_id (struct mptcp_sock *msk, -struct mptcp_addr_info *skc) +struct mptcp_pm_addr_entry *skc) { -struct mptcp_pm_addr_entry *entry =NULL , new_entry;__be16 msk_sport = ((struct inet_sock *)inet_sk ((struct sock *)msk))->inet_sport; +struct mptcp_pm_addr_entry *entry;spin_lock_bh (&msk->pm.lock); - entry =mptcp_userspace_pm_lookup_addr (msk, skc); + entry =mptcp_userspace_pm_lookup_addr (msk, &skc->addr);spin_unlock_bh (&msk->pm.lock);if (entry)return entry->addr.id; -memset (&new_entry,0 ,sizeof (struct mptcp_pm_addr_entry )); - new_entry.addr = *skc; - new_entry.addr.id =0 ; - new_entry.flags =MPTCP_PM_ADDR_FLAG_IMPLICIT ; +if (skc->addr.port == msk_sport) + skc->addr.port =0 ; -if (new_entry.addr.port == msk_sport) - new_entry.addr.port =0 ; - -return mptcp_userspace_pm_append_new_local_addr (msk, &new_entry, true); +return mptcp_userspace_pm_append_new_local_addr (msk, skc, true); } bool mptcp_userspace_pm_is_backup(struct mptcp_sock *msk,
net/mptcp/protocol.h +4 -2
@@ -1048,8 +1048,10 @@ bool mptcp_pm_add_addr_signal(struct mptbool mptcp_pm_rm_addr_signal (struct mptcp_sock *msk,unsigned int remaining,struct mptcp_rm_list *rm_list);int mptcp_pm_get_local_id (struct mptcp_sock *msk,struct sock_common *skc); -int mptcp_pm_nl_get_local_id (struct mptcp_sock *msk,struct mptcp_addr_info *skc); -int mptcp_userspace_pm_get_local_id (struct mptcp_sock *msk,struct mptcp_addr_info *skc); +int mptcp_pm_nl_get_local_id (struct mptcp_sock *msk, +struct mptcp_pm_addr_entry *skc); +int mptcp_userspace_pm_get_local_id (struct mptcp_sock *msk, +struct mptcp_pm_addr_entry *skc);bool mptcp_pm_is_backup (struct mptcp_sock *msk,struct sock_common *skc);bool mptcp_pm_nl_is_backup (struct mptcp_sock *msk,struct mptcp_addr_info *skc);bool mptcp_userspace_pm_is_backup (struct mptcp_sock *msk,struct mptcp_addr_info *skc);
Greg Kroah-Hartman
maintainerPATCH 252/261 mptcp: pm: userspace: fix use-after-free in get_local_id
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Geliang Tang <[email protected]>
[ Upstream commit 9bc6d5e4ca9f3cbb41d43400b3a31cb0403796c9 ]
In mptcp_pm_userspace_get_local_id(), the address entry is looked up under spinlock, but its id is read after dropping the lock. A concurrent deletion can free the entry between the unlock and the read, leading to UAF.
The race window is narrow. It was reproduced only with a locally constructed stress test that repeatedly overlaps an MP_JOIN SYN with a MPTCP_PM_CMD_SUBFLOW_DESTROY request.
However, the KASAN report below confirms that the race is reachable:
[ 666.319376] BUG: KASAN: slab-use-after-free in mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319386] Read of size 1 at addr ffff888124845610 by task swapper/0/0 ... [ 666.319401] Call Trace: [ 666.319405] <IRQ> [ 666.319408] dump_stack_lvl+0x53/0x70 [ 666.319412] print_address_description.constprop.0+0x2c/0x3b0 [ 666.319418] print_report+0xbe/0x2b0 [ 666.319421] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319423] kasan_report+0xce/0x100 [ 666.319426] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319429] mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319433] mptcp_pm_get_local_id+0x371/0x440 ... [ 666.319821] Allocated by task 45539: [ 666.319844] kasan_save_stack+0x33/0x60 [ 666.319855] kasan_save_track+0x14/0x30 [ 666.319858] __kasan_kmalloc+0x8f/0xa0 [ 666.319863] __kmalloc_noprof+0x1e7/0x520 [ 666.319867] sock_kmalloc+0xdf/0x130 [ 666.319885] sock_kmemdup+0x1b/0x40 [ 666.319888] mptcp_userspace_pm_append_new_local_addr+0x261/0x500 [ 666.319910] mptcp_pm_nl_announce_doit+0x16a/0x610 ... [ 666.319967] Freed by task 45560: [ 666.319988] kasan_save_stack+0x33/0x60 [ 666.319991] kasan_save_track+0x14/0x30 [ 666.319994] kasan_save_free_info+0x3b/0x60 [ 666.319998] __kasan_slab_free+0x43/0x70 [ 666.320000] kfree+0x166/0x440 [ 666.320003] sock_kfree_s+0x1d/0x50 [ 666.320007] mptcp_userspace_pm_delete_local_addr.isra.0+0x157/0x200 [ 666.320011] mptcp_pm_nl_subflow_destroy_doit+0x51d/0xea0
Fix by copying the id into a local variable while still holding the lock, and use -1 as a "not found" sentinel.
--- net/mptcp/pm_userspace.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/mptcp/pm_userspace.c +5 -2
@@ -153,12 +153,15 @@ int mptcp_userspace_pm_get_local_id(stru__be16 msk_sport = ((struct inet_sock *)inet_sk ((struct sock *)msk))->inet_sport;struct mptcp_pm_addr_entry *entry; +int id;spin_lock_bh (&msk->pm.lock); entry =mptcp_userspace_pm_lookup_addr (msk, &skc->addr); + id = entry ? entry->addr.id :-1 ;spin_unlock_bh (&msk->pm.lock); -if (entry) -return entry->addr.id; + +if (id !=-1 ) +return id;if (skc->addr.port == msk_sport) skc->addr.port =0 ;
Greg Kroah-Hartman
maintainerPATCH 253/261 sctp: close UDP tunnel sockets during netns teardown
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhiling Zou <[email protected]>
[ Upstream commit ffb2bd7ade36ec4da32c46a6eddbf4515316d08c ]
proc_sctp_do_udp_port() starts per-net SCTP UDP tunneling sockets when net.sctp.udp_port is set, and stops/restarts them when the sysctl value changes. The netns exit path does not stop these sockets, so a namespace can be torn down while its SCTP UDP tunnel sockets are still installed.
Close the UDP tunnel sockets from sctp_ctrlsock_exit() after unregistering the per-net sysctl table. This prevents new sysctl writes from racing in while the sockets are being released, and closes the sockets before the control socket is destroyed.
--- net/sctp/protocol.c | 1 + 1 file changed, 1 insertion(+)
net/sctp/protocol.c +1 -0
@@ -1471,6 +1471,7 @@ static int __net_init sctp_ctrlsock_init static void __net_exit sctp_ctrlsock_exit(struct net *net) {sctp_sysctl_net_unregister (net); +sctp_udp_sock_stop (net);/* Free the control endpoint. */ inet_ctl_sock_destroy (net->sctp.ctl_sock);
Greg Kroah-Hartman
maintainerPATCH 254/261 drm/i915/fbc: Extract intel_fbc_has_fences()
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ville Syrjälä <[email protected]>
[ Upstream commit bc34d310b578952d37b5200a7fa7475ab2a2bd5e ]
Pull the "do we have fences?" check into a single helper in the FBC code. Avoids having to call to outside the display code in multiple places for this.
Stable-dep-of: db9e64c983dc ("drm/i915/hdcp: require monotonically increasing seq_num_v")
--- drivers/gpu/drm/i915/display/intel_fbc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/gpu/drm/i915/display/intel_fbc.c +7 -2
@@ -223,6 +223,11 @@ static u16 intel_fbc_override_cfb_stridereturn 0 ; } +static bool intel_fbc_has_fences (struct drm_i915_private *i915) +{ +return intel_gt_support_legacy_fencing (to_gt (i915)); +} +static u32 i8xx_fbc_ctl (struct intel_fbc *fbc) {const struct intel_fbc_state *fbc_state = &fbc->state;
@@ -608,7 +613,7 @@ static void ivb_fbc_activate(struct inteelse if (DISPLAY_VER (i915) ==9 )skl_fbc_program_cfb_stride (fbc); -if (intel_gt_support_legacy_fencing (to_gt (i915))) +if (intel_fbc_has_fences (i915))snb_fbc_program_fence (fbc); intel_de_write(i915, ILK_DPFC_CONTROL(fbc->id),
@@ -992,7 +997,7 @@ static void intel_fbc_update_state(struc fbc_state->fence_y_offset =intel_plane_fence_y_offset (plane_state);drm_WARN_ON (&i915->drm, plane_state->flags &PLANE_HAS_FENCE && - !intel_gt_support_legacy_fencing (to_gt (i915))); + !intel_fbc_has_fences (i915));if (plane_state->flags &PLANE_HAS_FENCE ) fbc_state->fence_id =i915_vma_fence_id (plane_state->ggtt_vma);
Greg Kroah-Hartman
maintainerPATCH 255/261 drm/i915/hdcp: migrate away from kdev_to_i915() in bind/unbind
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit 3eac4684ecb5ea696bd283bd7f35e4829973f4f8 ]
Use to_intel_display() instead of kdev_to_i915() in the HDCP component API hooks. Avoid further drive-by changes at this point, and just convert the display pointer to i915, and leave the struct intel_display conversion for later.
Stable-dep-of: db9e64c983dc ("drm/i915/hdcp: require monotonically increasing seq_num_v")
--- drivers/gpu/drm/i915/display/intel_hdcp.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/gpu/drm/i915/display/intel_hdcp.c +6 -4
@@ -2108,10 +2108,11 @@ static void intel_hdcp_check_work(struct DRM_HDCP_CHECK_PERIOD_MS); } -static int i915_hdcp_component_bind (struct device *i915_kdev, +static int i915_hdcp_component_bind (struct device *drv_kdev,struct device *mei_kdev,void *data) { -struct drm_i915_private *i915 =kdev_to_i915 (i915_kdev); +struct intel_display *display =to_intel_display (drv_kdev); +struct drm_i915_private *i915 =to_i915 (display->drm);drm_dbg (&i915->drm,"I915 HDCP comp bind\n" );mutex_lock (&i915->display.hdcp.hdcp_mutex);
@@ -2122,10 +2123,11 @@ static int i915_hdcp_component_bind(strureturn 0 ; } -static void i915_hdcp_component_unbind (struct device *i915_kdev, +static void i915_hdcp_component_unbind (struct device *drv_kdev,struct device *mei_kdev,void *data) { -struct drm_i915_private *i915 =kdev_to_i915 (i915_kdev); +struct intel_display *display =to_intel_display (drv_kdev); +struct drm_i915_private *i915 =to_i915 (display->drm);drm_dbg (&i915->drm,"I915 HDCP comp unbind\n" );mutex_lock (&i915->display.hdcp.hdcp_mutex);
Greg Kroah-Hartman
maintainerPATCH 256/261 drm/i915/hdcp: Move to using intel_display in intel_hdcp
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Suraj Kandpal <[email protected]>
[ Upstream commit e35bf8f6a0ff06ceeff15bb032351cd5d006f92b ]
Move to using intel_display wherever possible in intel_hdcp.c as a part of code refactor.
--v2
-Move intel_display to the first line wherever possible [Jani] -use the closest reference when using to_intel_display [Jani]
Stable-dep-of: db9e64c983dc ("drm/i915/hdcp: require monotonically increasing seq_num_v")
--- drivers/gpu/drm/i915/display/intel_display_core.h | 3 drivers/gpu/drm/i915/display/intel_display_device.c | 3 drivers/gpu/drm/i915/display/intel_display_types.h | 40 +++ drivers/gpu/drm/i915/display/intel_hdcp.c | 250 +++++++++----------- 4 files changed, 170 insertions(+), 126 deletions(-)
drivers/gpu/drm/i915/display/intel_display_core.h +3 -0
@@ -267,6 +267,9 @@ struct intel_wm { };struct intel_display { +/* drm device backpointer */ +struct drm_device *drm; +/* Display functions */ struct {/* Top level crtc-ish functions */
drivers/gpu/drm/i915/display/intel_display_device.c +3 -0
@@ -857,6 +857,9 @@ intel_display_device_probe(struct drm_i9struct pci_dev *pdev =to_pci_dev (i915->drm.dev);int i; +/* Add drm device backpointer as early as possible. */ + i915->display.drm = &i915->drm; +if (has_gmdid)return probe_gmdid_display (i915, gmdid_ver, gmdid_rel, gmdid_step);
drivers/gpu/drm/i915/display/intel_display_types.h +40 -0
@@ -2112,4 +2112,44 @@ to_intel_frontbuffer(struct drm_framebuf return fb ? to_intel_framebuffer(fb)->frontbuffer : NULL; } +/* +* Conversion functions/macros from various pointer types to struct +* intel_display pointer. +*/ +#define __drm_device_to_intel_display (p) \ + ((p) ? &to_i915(p)->display : NULL) +#define __device_to_intel_display (p) \ + __drm_device_to_intel_display(&kdev_to_i915(p)->drm) +#define __intel_connector_to_intel_display (p) \ + __drm_device_to_intel_display((p)->base.dev) +#define __intel_crtc_to_intel_display (p) \ + __drm_device_to_intel_display((p)->base.dev) +#define __intel_crtc_state_to_intel_display (p) \ + __drm_device_to_intel_display((p)->uapi.crtc->dev) +#define __intel_digital_port_to_intel_display (p) \ + __drm_device_to_intel_display((p)->base.base.dev) +#define __intel_dp_to_intel_display (p) \ + __drm_device_to_intel_display(dp_to_dig_port(p)->base.base.dev) +#define __intel_encoder_to_intel_display (p) \ + __drm_device_to_intel_display((p)->base.dev) +#define __intel_hdmi_to_intel_display (p) \ + __drm_device_to_intel_display(hdmi_to_dig_port(p)->base.base.dev) + +/* Helper for generic association. Map types to conversion functions/macros. */ +#define __assoc (type, p) \ + struct type: __##type##_to_intel_display((struct type *)(p)) + +/* Convert various pointer types to struct intel_display pointer. */ +#define to_intel_display (p) \ + _Generic(*p, \ + __assoc(drm_device, p), \ + __assoc(device, p), \ + __assoc(intel_connector, p), \ + __assoc(intel_crtc, p), \ + __assoc(intel_crtc_state, p), \ + __assoc(intel_digital_port, p), \ + __assoc(intel_dp, p), \ + __assoc(intel_encoder, p), \ + __assoc(intel_hdmi, p)) +#endif /* __INTEL_DISPLAY_TYPES_H__ */
drivers/gpu/drm/i915/display/intel_hdcp.c +124 -126
729 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 257/261 drm/i915/hdcp: require monotonically increasing seq_num_v
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit db9e64c983dcb07ff256bd455f258c44aa530ff8 ]
The HDCP 2.2 specification requires the seq_num_v to be monotonically increasing, and repeated seq_num_v needs to be treated as an integrity failure. Make it so.
For the first message, seq_num_v must be zero, and is already checked. We can only check for less-than-or-equal for the subsequent messages, where hdcp2_encrypted is true.
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/display/intel_hdcp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
drivers/gpu/drm/i915/display/intel_hdcp.c +4 -3
@@ -1654,9 +1654,10 @@ int hdcp2_authenticate_repeater_topologyreturn -EINVAL ; } -if (seq_num_v < hdcp->seq_num_v) { -/* Roll over of the seq_num_v from repeater. Reauthenticate. */ -drm_dbg_kms (display->drm,"Seq_num_v roll over.\n" ); +if (hdcp->hdcp2_encrypted && seq_num_v <= hdcp->seq_num_v) { +/* Reauthenticate on Seq_num_v repeat or rollover */ +drm_dbg_kms (display->drm,"Seq_num_v %s\n" , + seq_num_v == hdcp->seq_num_v ?"repeat" :"rollover" );return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 258/261 drm/i915/hdcp: check streams[] bounds before overflow
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jani Nikula <[email protected]>
[ Upstream commit bbb15a6b042d02e5508a02b4847e02d2579ee7bc ]
The data->streams[] overflow check is done after the buffer overflow has already happened. Move the overflow check before the write.
Side note, emitting a warning splat with a backtrace might be overkill here, but prefer not changing the behaviour other than not doing the overrun.
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/display/intel_hdcp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/display/intel_hdcp.c +4 -1
@@ -2321,6 +2321,9 @@ intel_hdcp_set_streams(struct intel_digiif (conn_dig_port != dig_port)continue ; +if (drm_WARN_ON (&i915->drm, data->k >=INTEL_NUM_PIPES (i915))) +return -EINVAL ; + data->streams[data->k].stream_id =intel_conn_to_vcpi (&state->base, connector); data->k++;
@@ -2331,7 +2334,7 @@ intel_hdcp_set_streams(struct intel_digi }drm_connector_list_iter_end (&conn_iter); -if (drm_WARN_ON (&i915->drm, data->k >INTEL_NUM_PIPES (i915) || data->k ==0 )) +if (drm_WARN_ON (&i915->drm, !data->k))return -EINVAL ;return 0 ;
Greg Kroah-Hartman
maintainerPATCH 259/261 ALSA: hda: codecs: hdmi: disable keep-alive before audio format change
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kai Vehmanen <[email protected]>
[ Upstream commit a3d6d3cedfe87bbd5a677d52b22ac20d28e59cf8 ]
When a keep-alive (KAE) silent stream is active on an Intel HDMI/DP codec, opening a real PCM stream reprograms the converter format and the audio infoframe in snd_hda_hdmi_generic_pcm_prepare(). Part of that reprogramming - the converter channel count and the channel mapping in snd_hda_hdmi_setup_audio_infoframe() - is not safe to do while a keep-alive stream is active. This is most visible when switching to a multichannel PCM configuration, where the active channel count actually changes. In that case the newly opened PCM stream plays no sound.
Add an optional hdmi_ops .prepare hook, called at the start of the PCM prepare sequence (before the format and infoframe are touched), and implement it for HSW+ to release keep-alive. Keep-alive is then re-enabled as before once the new stream has been set up, in the setup_stream op.
--- sound/pci/hda/patch_hdmi.c | 48 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-)
sound/pci/hda/patch_hdmi.c +42 -6
@@ -111,6 +111,15 @@ struct hdmi_ops { hda_nid_t pin_nid, int dev_id, u32 stream_tag, int format); +/* +* Optional hook invoked at the beginning of the PCM prepare +* sequence, before the audio infoframe and stream format are +* (re)programmed. Used to disable keep-alive / silent stream so +* that the format change is not done while keep-alive is active. +*/ +void (*prepare)(struct hda_codec *codec, +struct hdmi_spec_per_pin *per_pin); +void (*pin_cvt_fixup)(struct hda_codec *codec,struct hdmi_spec_per_pin *per_pin,hda_nid_t cvt_nid);
@@ -2134,6 +2143,9 @@ static int generic_hdmi_playback_pcm_pre per_pin->channels = substream->runtime->channels; per_pin->setup = true; +if (spec->ops.prepare) + spec->ops.prepare (codec, per_pin); +if (get_wcaps (codec, cvt_nid) &AC_WCAP_STRIPE ) { stripe =snd_hdac_get_stream_stripe_ctl (&codec->bus->core, substream);
@@ -2905,6 +2917,28 @@ static void register_i915_notifier(struc codec->relaxed_resume =1 ; } +/* +* prepare ops override for HSW+ +* +* Disable keep-alive before the converter format and audio infoframe are +* reprogrammed by the PCM prepare sequence. Changing the audio format (e.g. +* the channel count when switching to multichannel PCM) while a keep-alive +* stream is active is not safe, so release keep-alive here, early in the +* sequence. It is re-enabled once the new stream has been set up, in +* i915_hsw_setup_stream(). +*/ +static void i915_hsw_prepare (struct hda_codec *codec, +struct hdmi_spec_per_pin *per_pin) +{ +struct hdmi_spec *spec = codec->spec; + +if (spec->silent_stream_type ==SILENT_STREAM_KAE && per_pin->silent_stream) { +silent_stream_set_kae (codec, per_pin, false); +/* wait for pending transfers in codec to clear */ +usleep_range (100 ,200 ); + } +} +/* setup_stream ops override for HSW+ */ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, int dev_id, u32 stream_tag,
@@ -2922,15 +2956,16 @@ static int i915_hsw_setup_stream(structhaswell_verify_D0 (codec, cvt_nid, pin_nid); -if (spec->silent_stream_type ==SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { -silent_stream_set_kae (codec, per_pin, false); -/* wait for pending transfers in codec to clear */ -usleep_range (100 ,200 ); - } - res =hdmi_setup_stream (codec, cvt_nid, pin_nid, dev_id, stream_tag, format); +/* +* Keep-alive was disabled in i915_hsw_prepare(), re-enable it now. +* The pin lookup above resolves to the same per_pin that prepare +* used (pin_nid comes from that per_pin), so this stays balanced; a +* NULL per_pin only occurs on a lookup failure that also implies no +* active keep-alive stream to restore. +*/ if (spec->silent_stream_type ==SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {usleep_range (100 ,200 );silent_stream_set_kae (codec, per_pin, true);
@@ -3106,6 +3141,7 @@ static int intel_hsw_common_init(struct codec->depop_delay =0 ; codec->auto_runtime_pm =1 ; + spec->ops.prepare = i915_hsw_prepare; spec->ops.setup_stream = i915_hsw_setup_stream; spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
Greg Kroah-Hartman
maintainerPATCH 260/261 drm/tegra: fbdev: Do not assign to struct drm_fb_helper.info
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Zimmermann <[email protected]>
commit d23bd83f3e47a928e783c0d6a004737519dc77dc upstream.
That field already contains the value being assigned. No need to do this twice.
--- drivers/gpu/drm/tegra/fbdev.c | 1 - 1 file changed, 1 deletion(-)
drivers/gpu/drm/tegra/fbdev.c +0 -1
@@ -105,7 +105,6 @@ static int tegra_fbdev_probe(struct drm_ } helper->fb = fb; - helper->info = info; info->fbops = &tegra_fb_ops;
Greg Kroah-Hartman
maintainerPATCH 261/261 usb: typec: ucsi: Correct teardown ordering in ucsi_init() error path
In reply to Greg Kroah-Hartman6.6-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrei Kuchynski <[email protected]>
commit fb0bf289f5d529336ef490c8273e88a8a8b29f69 upstream.
The commit 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration") consolidated port teardown into the ucsi_unregister_port() helper. However, it introduced an ordering problem in the ucsi_init() error path.
Fix this by ensuring ucsi_unregister_port() is called before we unregister their corresponding lockdep keys.
--- drivers/usb/typec/ucsi/ucsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/usb/typec/ucsi/ucsi.c +2 -2
@@ -1548,11 +1548,11 @@ static int ucsi_init(struct ucsi *ucsi)return 0 ; err_unregister: +for (con = connector; con->port; con++) +ucsi_unregister_port (con);for (i =0 ; i < ucsi->cap.num_connectors; i++)lockdep_unregister_key (&connector[i].lock_key); -for (con = connector; con->port; con++) -ucsi_unregister_port (con);kfree (connector); err_reset:memset (&ucsi->cap,0 ,sizeof (ucsi->cap));
Hi!
Quoted 4 lines
This is the start of the stable review cycle for the 6.6.151 release. There are 261 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
CIP testing did not find any problems here:
https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/tree/linux-6.6.y
Best regards,
Pavel
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.6.151 release. There are 261 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sun, 09 Aug 2026 14:33:46 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 9 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.6.151-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.6.y
and the diffstat can be found below.thanks,
greg k-h
Compiled and booted on my test system. No dmesg regressions.
thanks,
-- Shuah
Am 07.08.2026 um 16:35 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 6.6.151 release. There are 261 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697 v2 server. No dmesg oddities or regressions found.
Beste Grüße,
Peter Schneider
Signature · 8 lines
Climb the mountain not to plant your flag, but to embrace the challenge, enjoy the air and behold the view. Climb it so you can see the world, not so the world can see you. -- David McCullough Jr. OpenPGP: 0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244 Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc https://keys.mailvelope.com/pks/lookup?op=get&[email protected] https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
Build tested in our x86,arm64,loongarch,riscv config successfully without error.
Best Regards
Wentao Guan
Log:
Linux version 6.6.151-rc1-arm64-desktop-hwe-g1e59060c56f6 (guanwentao@uos-PC) (aarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT Linux version 6.6.151-rc1-arm64-desktop-hwe-g1e59060c56f6 (guanwentao@uos-PC) (aarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #2 SMP PREEMPT Sat Aug 8 08:29:57 CST 2026 Linux version 6.6.151-rc1-loong64-desktop-hwe-g1e59060c56f6 (guanwentao@uos-PC) (loongarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT Linux version 6.6.151-rc1-loong64-desktop-hwe-g1e59060c56f6 (guanwentao@uos-PC) (loongarch64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #3 SMP PREEMPT Sat Aug 8 08:46:29 CST 2026 Linux version 6.6.151-rc1-riscv64-desktop-hwe+ (guanwentao@uos-PC) (riscv64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP
Linux version 6.6.151-rc1-riscv64-desktop-hwe+ (guanwentao@uos-PC) (riscv64-linux-gnu-gcc-12 (Deepin 12.3.0-17deepin8) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #4 SMP Sat Aug 8 09:02:19 CST 2026 Linux version 6.6.151-rc1-amd64-desktop-hwe-g1e59060c56f6 (guanwentao@uos-PC) (gcc (Deepin 12.3.0-17deepin15) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) # SMP PREEMPT_DYNAMIC
Linux version 6.6.151-rc1-amd64-desktop-hwe-g1e59060c56f6 (guanwentao@uos-PC) (gcc (Deepin 12.3.0-17deepin15) 12.3.0, GNU ld (GNU Binutils for Deepin) 2.41) #1 SMP PREEMPT_DYNAMIC Sat Aug 8 08:11:57 CST 2026
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 6.6.151-rc1-g1e59060c56f6 #1 SMP PREEMPT_DYNAMIC Sat Aug 8 10:16:17 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.6.151 release. There are 261 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sun, 09 Aug 2026 14:33:46 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.6.151-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.6.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
Quoted 8 lines · On Fri, 07 Aug 2026 16:35:57 +0200 Greg Kroah-Hartman…
This is the start of the stable review cycle for the 6.6.151 release. There are 261 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sun, 09 Aug 2026 14:33:46 +0000. Anything received after that time might be too late.
Boot-tested under QEMU for Rust x86_64:
Thanks!
Cheers,
Miguel
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.6.151 release. There are 261 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.6.151 release. There are 261 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Sun, 09 Aug 2026 14:33:46 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.6.151-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.6.y
and the diffstat can be found below.thanks,
greg k-h
On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:
Signature · 1 line
Florian