5.15.216-rc1 review
Greg Kroah-Hartman
maintainerPATCH 135/456 can: bcm: add locking when updating filter and timer values
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 749179c2e25b95d22499ed29096b3e02d6dfd2b4 upstream.
KCSAN detected a simultaneous access to timer values that can be overwritten in bcm_rx_setup() when updating timer and filter content while bcm_rx_handler(), bcm_rx_timeout_handler() or bcm_rx_thr_handler() run concurrently on incoming CAN traffic.
Protect the timer (ival1/ival2/kt_ival1/kt_ival2/kt_lastmsg) and filter (nframes/flags/frames/last_frames) updates in bcm_rx_setup() with a new per-op bcm_rx_update_lock, taken with the matching scope in the RX handlers. memcpy_from_msg() is staged into a temporary buffer before the lock is taken, since it can sleep and must not run under a spinlock.
hrtimer_cancel() is always called without bcm_rx_update_lock held, since bcm_rx_timeout_handler()/bcm_rx_thr_handler() take the same lock and a running callback would otherwise deadlock against the canceller.
Also close a related race: bcm_rx_setup() cleared the RTR flag in the stored reply frame's can_id as a separate, unprotected step after the frame content was already installed, so a concurrent bcm_rx_handler() could transmit a stale reply with CAN_RTR_FLAG still set. Fold that normalization into the initial frame preparation instead (on the staged buffer for updates, directly on op->frames pre-registration for new ops), so the installed frame is always atomically self-consistent.
bcm_rx_handler()'s RX_RTR_FRAME check now takes a lock-protected snapshot of op->flags before deciding whether to call bcm_can_tx(), but does not hold the lock across that call.
Also take a lock-protected snapshot of the currframe in bcm_can_tx() to avoid partly overwrites by content updates in bcm_tx_setup(). Finally check if a TX_RESET_MULTI_IDX/SETTIMER might have reset op->currframe between the two locked sections in bcm_can_tx().
Omit calling hrtimer_forward() with zero interval in bcm_rx_thr_handler(). kt_ival2 may have been concurrently cleared by bcm_rx_setup() before it cancels this timer, so check kt_ival2 inside the bcm_rx_update_lock.
--- net/can/bcm.c | 178 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 134 insertions(+), 44 deletions(-)
net/can/bcm.c +134 -44
@@ -126,6 +126,7 @@ struct bcm_op {struct sock *sk;struct net_device *rx_reg_dev;spinlock_t bcm_tx_lock;/* protect currframe/count in runtime updates */ +spinlock_t bcm_rx_update_lock;/* protect filter/timer data updates */ };struct bcm_sock {
@@ -280,21 +281,27 @@ static int bcm_proc_show(struct seq_file *m, void *v)* bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op */ -static void bcm_can_tx (struct bcm_op *op) +static void bcm_can_tx (struct bcm_op *op,struct canfd_frame *cf) {struct sk_buff *skb;struct net_device *dev; -struct canfd_frame *cf; +struct canfd_frame cframe; +bool cyclic = !cf; +unsigned int idx =0 ;int err;/* no target device? => exit */ if (!op->ifindex)return ; -/* read currframe under lock protection */ -spin_lock_bh (&op->bcm_tx_lock); - cf = op->frames + op->cfsiz * op->currframe; -spin_unlock_bh (&op->bcm_tx_lock); +if (cyclic) { +/* read currframe under lock protection */ +spin_lock_bh (&op->bcm_tx_lock); + idx = op->currframe; +memcpy (&cframe, op->frames + op->cfsiz * idx, op->cfsiz); + cf = &cframe; +spin_unlock_bh (&op->bcm_tx_lock); + } dev =dev_get_by_index (sock_net (op->sk), op->ifindex);if (!dev) {
@@ -323,14 +330,20 @@ static void bcm_can_tx(struct bcm_op *op)if (!err) op->frames_abs++; - op->currframe++; +/* only advance the cyclic sequence if nothing reset currframe while +* we were sending - a concurrent TX_RESET_MULTI_IDX means this +* frame's bookkeeping belongs to a sequence that no longer exists +*/ +if (!cyclic || op->currframe == idx) { + op->currframe++; -/* reached last frame? */ -if (op->currframe >= op->nframes) - op->currframe =0 ; +/* reached last frame? */ +if (op->currframe >= op->nframes) + op->currframe =0 ; -if (op->count >0 ) - op->count--; +if (op->count >0 ) + op->count--; + }spin_unlock_bh (&op->bcm_tx_lock); out:
@@ -429,7 +442,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)struct bcm_msg_head msg_head;if (op->kt_ival1 && (op->count >0 )) { -bcm_can_tx (op); +bcm_can_tx (op,NULL );if (!op->count && (op->flags &TX_COUNTEVT )) {/* create notification to user */
@@ -446,7 +459,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) } }else if (op->kt_ival2) { -bcm_can_tx (op); +bcm_can_tx (op,NULL ); } return bcm_tx_set_expiry(op, &op->timer) ?
@@ -585,6 +598,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);struct bcm_msg_head msg_head; +spin_lock_bh (&op->bcm_rx_update_lock); +/* if user wants to be informed, when cyclic CAN-Messages come back */ if ((op->flags &RX_ANNOUNCE_RESUME ) && op->last_frames) {/* clear received CAN frames to indicate 'nothing received' */
@@ -601,6 +616,8 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer) msg_head.can_id = op->can_id; msg_head.nframes =0 ; +spin_unlock_bh (&op->bcm_rx_update_lock); +bcm_send_to_user (op, &msg_head,NULL ,0 );return HRTIMER_NORESTART ;
@@ -649,15 +666,26 @@ static int bcm_rx_thr_flush(struct bcm_op *op)static enum hrtimer_restart bcm_rx_thr_handler (struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, thrtimer); +enum hrtimer_restart ret; -if (bcm_rx_thr_flush (op)) { -hrtimer_forward (hrtimer,ktime_get (), op->kt_ival2); -return HRTIMER_RESTART ; +spin_lock_bh (&op->bcm_rx_update_lock); + +/* kt_ival2 may have been concurrently cleared by bcm_rx_setup() +* before it cancels this timer - never forward with a zero +* interval in that case. +*/ +if (bcm_rx_thr_flush (op) && op->kt_ival2) { +hrtimer_forward_now (hrtimer, op->kt_ival2); + ret =HRTIMER_RESTART ; }else {/* rearm throttle handling */ op->kt_lastmsg =0 ; -return HRTIMER_NORESTART ; + ret =HRTIMER_NORESTART ; } + +spin_unlock_bh (&op->bcm_rx_update_lock); + +return ret; }/*
@@ -667,7 +695,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) {struct bcm_op *op = (struct bcm_op *)data;const struct canfd_frame *rxframe = (struct canfd_frame *)skb->data; +struct canfd_frame rtrframe;unsigned int i; +bool rtr_frame;if (op->can_id != rxframe->can_id)return ;
@@ -686,12 +716,23 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)/* update statistics */ op->frames_abs++; -if (op->flags &RX_RTR_FRAME ) { +/* snapshot the flag under lock: op->flags/op->frames may be updated +* concurrently by bcm_rx_setup(). +*/ +spin_lock_bh (&op->bcm_rx_update_lock); + rtr_frame = op->flags &RX_RTR_FRAME ; +if (rtr_frame) +memcpy (&rtrframe, op->frames, op->cfsiz); +spin_unlock_bh (&op->bcm_rx_update_lock); + +if (rtr_frame) {/* send reply for RTR-request (placed in op->frames[0]) */ -bcm_can_tx (op); +bcm_can_tx (op, &rtrframe);return ; } +spin_lock_bh (&op->bcm_rx_update_lock); +if (op->flags &RX_FILTER_ID ) {/* the easiest case */ bcm_rx_update_and_send (op, op->last_frames, rxframe);
@@ -725,6 +766,8 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) rx_starttimer:bcm_rx_starttimer (op); + +spin_unlock_bh (&op->bcm_rx_update_lock); }/*
@@ -1068,7 +1111,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,list_add_rcu (&op->list, &bo->tx_ops);if (op->flags &TX_ANNOUNCE ) -bcm_can_tx (op); +bcm_can_tx (op,NULL );if (op->flags &STARTTIMER )bcm_tx_start_timer (op);
@@ -1082,6 +1125,24 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,return err; } +static void bcm_rx_setup_rtr_check (struct bcm_msg_head *msg_head, +struct bcm_op *op,void *new_frames) +{ +/* funny feature in RX(!)_SETUP only for RTR-mode: +* copy can_id into frame BUT without RTR-flag to +* prevent a full-load-loopback-test ... ;-] +* normalize this on the staged buffer, before it is +* ever installed into op->frames. +*/ +if (msg_head->flags &RX_RTR_FRAME ) { +struct canfd_frame *frame0 = new_frames; + +if ((msg_head->flags &TX_CP_CAN_ID ) || + frame0->can_id == op->can_id) + frame0->can_id = op->can_id & ~CAN_RTR_FLAG ; + } +} +/* * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg) */
@@ -1116,6 +1177,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,/* check the given can_id */ op =bcm_find_op (&bo->rx_ops, msg_head, ifindex);if (op) { +void *new_frames =NULL ; +/* update existing BCM operation */ /*
@@ -1127,19 +1190,48 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,return -E2BIG ;if (msg_head->nframes) { -/* update CAN frames content */ - err =memcpy_from_msg (op->frames, msg, +/* get new CAN frames content before locking */ + new_frames =kmalloc (msg_head->nframes * op->cfsiz, +GFP_KERNEL ); +if (!new_frames) +return -ENOMEM ; + + err =memcpy_from_msg (new_frames, msg, msg_head->nframes * op->cfsiz); -if (err <0 ) +if (err <0 ) { +kfree (new_frames);return err; + } -/* clear last_frames to indicate 'nothing received' */ -memset (op->last_frames,0 , msg_head->nframes * op->cfsiz); +bcm_rx_setup_rtr_check (msg_head, op, new_frames); } +spin_lock_bh (&op->bcm_rx_update_lock); op->nframes = msg_head->nframes; op->flags = msg_head->flags; +if (msg_head->nframes) { +/* update CAN frames content */ +memcpy (op->frames, new_frames, + msg_head->nframes * op->cfsiz); + +/* clear last_frames to indicate 'nothing received' */ +memset (op->last_frames,0 , + msg_head->nframes * op->cfsiz); + } + +if (msg_head->flags &SETTIMER ) { + op->ival1 = msg_head->ival1; + op->ival2 = msg_head->ival2; + op->kt_ival1 =bcm_timeval_to_ktime (msg_head->ival1); + op->kt_ival2 =bcm_timeval_to_ktime (msg_head->ival2); + op->kt_lastmsg =0 ; + } +spin_unlock_bh (&op->bcm_rx_update_lock); + +/* free temporary frames / kfree(NULL) is safe */ +kfree (new_frames); +/* Only an update -> do not call can_rx_register() */ do_rx_register =0 ;
@@ -1150,6 +1242,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,return -ENOMEM ;spin_lock_init (&op->bcm_tx_lock); +spin_lock_init (&op->bcm_rx_update_lock); op->can_id = msg_head->can_id; op->nframes = msg_head->nframes; op->cfsiz =CFSIZ (msg_head->flags);
@@ -1191,6 +1284,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,kfree (op);return err; } + +bcm_rx_setup_rtr_check (msg_head, op, op->frames); }/* bcm_can_tx / bcm_tx_timeout_handler needs this */
@@ -1218,29 +1313,22 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,/* check flags */ if (op->flags &RX_RTR_FRAME ) { -struct canfd_frame *frame0 = op->frames; -/* no timers in RTR-mode */ hrtimer_cancel (&op->thrtimer);hrtimer_cancel (&op->timer); - -/* -* funny feature in RX(!)_SETUP only for RTR-mode: -* copy can_id into frame BUT without RTR-flag to -* prevent a full-load-loopback-test ... ;-] -*/ -if ((op->flags &TX_CP_CAN_ID ) || - (frame0->can_id == op->can_id)) - frame0->can_id = op->can_id & ~CAN_RTR_FLAG ; - }else {if (op->flags &SETTIMER ) { -/* set timer value */ - op->ival1 = msg_head->ival1; - op->ival2 = msg_head->ival2; - op->kt_ival1 =bcm_timeval_to_ktime (msg_head->ival1); - op->kt_ival2 =bcm_timeval_to_ktime (msg_head->ival2); +/* set timers (locked) for newly created op */ +if (do_rx_register) { +spin_lock_bh (&op->bcm_rx_update_lock); + op->ival1 = msg_head->ival1; + op->ival2 = msg_head->ival2; + op->kt_ival1 =bcm_timeval_to_ktime (msg_head->ival1); + op->kt_ival2 =bcm_timeval_to_ktime (msg_head->ival2); + op->kt_lastmsg =0 ; +spin_unlock_bh (&op->bcm_rx_update_lock); + }/* disable an active timer due to zero value? */ if (!op->kt_ival1)
@@ -1250,9 +1338,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,* In any case cancel the throttle timer, flush * potentially blocked msgs and reset throttle handling */ - op->kt_lastmsg =0 ;hrtimer_cancel (&op->thrtimer); + +spin_lock_bh (&op->bcm_rx_update_lock);bcm_rx_thr_flush (op); +spin_unlock_bh (&op->bcm_rx_update_lock); }if ((op->flags &STARTTIMER ) && op->kt_ival1)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 136/456 can: bcm: fix CAN frame rx/tx statistics
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit e6c24ba95fc3f1b5e1dcd28b1c6e59ef61a9daa5 upstream.
KCSAN detected a data race within the bcm_rx_handler() when two CAN frames have been simultaneously received and processed in a single rx op by two different CPUs.
Use atomic operations with (signed) long data types to access the statistics in the hot path to fix the KCSAN complaint.
Additionally simplify the update and check of statistics overflow by using the atomic operations in separate bcm_update_[rx|tx]_stats() functions. The rx variant runs under bcm_rx_update_lock to prevent races when resetting the two rx counters; the tx variant runs under bcm_tx_lock and only needs to guard its own counter's overflow.
As the rx path resets its values already at LONG_MAX / 100, there is no conflict between the two locking domains (bcm_rx_update_lock vs. bcm_tx_lock) even for ops that use both paths.
The rx statistics update and the frames_filtered update in bcm_rx_changed() were previously performed in two separate bcm_rx_update_lock sections. For an rx op subscribed on all interfaces (ifindex == 0), bcm_rx_handler() can run concurrently on different CPUs, so a counter reset by one CPU between these two sections could leave frames_filtered larger than frames_abs on another CPU, producing a bogus (even negative) reduction percentage in procfs. Update the statistics in the same critical section as bcm_rx_changed() to close this gap, which also removes the now unneeded extra lock/unlock pair around the traffic_flags calculation.
--- net/can/bcm.c | 67 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 22 deletions(-)
net/can/bcm.c +45 -22
@@ -109,7 +109,7 @@ struct bcm_op {int ifindex;canid_t can_id;u32 flags; -unsigned long frames_abs, frames_filtered; +atomic_long_t frames_abs, frames_filtered;struct bcm_timeval ival1, ival2;struct hrtimer timer, thrtimer;ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;
@@ -216,10 +216,13 @@ static int bcm_proc_show(struct seq_file *m, void *v)list_for_each_entry_rcu (op, &bo->rx_ops, list) { -unsigned long reduction; +long reduction, frames_filtered, frames_abs; + + frames_filtered =atomic_long_read (&op->frames_filtered); + frames_abs =atomic_long_read (&op->frames_abs);/* print only active entries & prevent division by zero */ -if (!op->frames_abs) +if (!frames_abs)continue ; seq_printf(m, "rx_op: %03X %-5s ", op->can_id,
@@ -241,9 +244,9 @@ static int bcm_proc_show(struct seq_file *m, void *v) (long long)ktime_to_us(op->kt_ival2));seq_printf (m,"# recv %ld (%ld) => reduction: " , - op->frames_filtered, op->frames_abs); + frames_filtered, frames_abs); - reduction =100 - (op->frames_filtered *100 ) / op->frames_abs; + reduction =100 - (frames_filtered *100 ) / frames_abs;seq_printf (m,"%s%ld%%\n" , (reduction ==100 ) ?"near " :"" , reduction);
@@ -267,7 +270,8 @@ static int bcm_proc_show(struct seq_file *m, void *v)seq_printf (m,"t2=%lld " , (long long )ktime_to_us (op->kt_ival2)); -seq_printf (m,"# sent %ld\n" , op->frames_abs); +seq_printf (m,"# sent %ld\n" , +atomic_long_read (&op->frames_abs)); }seq_putc (m,'\n' );
@@ -277,6 +281,24 @@ static int bcm_proc_show(struct seq_file *m, void *v) }#endif /* CONFIG_PROC_FS */ +static void bcm_update_rx_stats (struct bcm_op *op) +{ +/* prevent overflow of the reduction% calculation in bcm_proc_show() */ +if (atomic_long_inc_return (&op->frames_abs) >LONG_MAX /100 ) { +atomic_long_set (&op->frames_filtered,0 ); +atomic_long_set (&op->frames_abs,0 ); + } +} + +static void bcm_update_tx_stats (struct bcm_op *op) +{ +/* tx_op has no reduction% calculation - use the full range and +* just keep the displayed counter non-negative on overflow +*/ +if (atomic_long_inc_return (&op->frames_abs) ==LONG_MAX ) +atomic_long_set (&op->frames_abs,0 ); +} +/* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface * of the given bcm tx op
@@ -328,7 +350,7 @@ static void bcm_can_tx(struct bcm_op *op, struct canfd_frame *cf)spin_lock_bh (&op->bcm_tx_lock);if (!err) - op->frames_abs++; +bcm_update_tx_stats (op);/* only advance the cyclic sequence if nothing reset currframe while * we were sending - a concurrent TX_RESET_MULTI_IDX means this
@@ -473,12 +495,9 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data) {struct bcm_msg_head head; -/* update statistics */ - op->frames_filtered++; - -/* prevent statistics overflow */ -if (op->frames_filtered >ULONG_MAX /100 ) - op->frames_filtered = op->frames_abs =0 ; +/* update statistics (frames_filtered <= frames_abs) */ +if (atomic_long_read (&op->frames_abs)) +atomic_long_inc (&op->frames_filtered);/* this element is not throttled anymore */ data->flags &= (BCM_CAN_FLAGS_MASK |RX_RECV );
@@ -713,25 +732,29 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data) op->rx_stamp = skb->tstamp;/* save originator for recvfrom() */ op->rx_ifindex = skb->dev->ifindex; -/* update statistics */ - op->frames_abs++; -/* snapshot the flag under lock: op->flags/op->frames may be updated -* concurrently by bcm_rx_setup(). -*/ +/* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */ spin_lock_bh (&op->bcm_rx_update_lock); + rtr_frame = op->flags &RX_RTR_FRAME ; -if (rtr_frame) +if (rtr_frame) { +bcm_update_rx_stats (op); +/* snapshot RTR content under lock */ memcpy (&rtrframe, op->frames, op->cfsiz); -spin_unlock_bh (&op->bcm_rx_update_lock); +spin_unlock_bh (&op->bcm_rx_update_lock); -if (rtr_frame) {/* send reply for RTR-request (placed in op->frames[0]) */ bcm_can_tx (op, &rtrframe);return ; } -spin_lock_bh (&op->bcm_rx_update_lock); +/* update statistics in the same critical section as bcm_rx_changed() +* below: frames_filtered must never be checked/incremented against a +* frames_abs snapshot from a concurrent bcm_rx_handler() call on +* another CPU for the same (wildcard) op, or frames_filtered can end +* up larger than frames_abs. +*/ +bcm_update_rx_stats (op);if (op->flags &RX_FILTER_ID ) {/* the easiest case */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 137/456 can: bcm: extend bcm_tx_lock usage for data and timer updates
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 12ce799f7ab1e05bd8fbf79e46f403bfe5597ebc upstream.
Stage new CAN frame content for an existing tx op into a kmalloc()'d buffer and validate it there, mirroring the approach already used in bcm_rx_setup(). Only copy the validated data into op->frames while holding op->bcm_tx_lock, so bcm_can_tx() and bcm_tx_timeout_handler() can no longer observe a partially updated or unvalidated frame.
Add a missing error path for memcpy_from_msg() when copying CAN frame data from userspace.
Also move the kt_ival1/kt_ival2/ival1/ival2 updates in bcm_tx_setup() under op->bcm_tx_lock, and read kt_ival1/kt_ival2/count under the same lock in bcm_tx_set_expiry() and bcm_tx_timeout_handler(), closing the torn 64-bit ktime_t read on 32-bit platforms.
--- net/can/bcm.c | 104 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 29 deletions(-)
net/can/bcm.c +75 -29
@@ -125,7 +125,7 @@ struct bcm_op {struct canfd_frame last_sframe;struct sock *sk;struct net_device *rx_reg_dev; -spinlock_t bcm_tx_lock;/* protect currframe/count in runtime updates */ +spinlock_t bcm_tx_lock;/* protect tx data and timer updates */ spinlock_t bcm_rx_update_lock;/* protect filter/timer data updates */ };
@@ -440,12 +440,18 @@ static bool bcm_tx_set_expiry(struct bcm_op *op, struct hrtimer *hrt) {ktime_t ival; +spin_lock_bh (&op->bcm_tx_lock); +if (op->kt_ival1 && op->count) ival = op->kt_ival1; -else if (op->kt_ival2) +else if (op->kt_ival2) { ival = op->kt_ival2; -else + }else { +spin_unlock_bh (&op->bcm_tx_lock);return false; + } + +spin_unlock_bh (&op->bcm_tx_lock);hrtimer_set_expires (hrt,ktime_add (ktime_get (), ival));return true;
@@ -462,25 +468,47 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer) { struct bcm_op *op = container_of(hrtimer, struct bcm_op, timer);struct bcm_msg_head msg_head; +bool tx_ival1, tx_ival2; + +/* snapshot kt_ival1/kt_ival2/count under lock to avoid torn +* ktime_t reads racing with concurrent bcm_tx_setup() updates +*/ +spin_lock_bh (&op->bcm_tx_lock); + tx_ival1 = op->kt_ival1 && (op->count >0 ); + tx_ival2 = !!op->kt_ival2; +spin_unlock_bh (&op->bcm_tx_lock); + +if (tx_ival1) { +u32 flags, count; +struct bcm_timeval ival1, ival2; -if (op->kt_ival1 && (op->count >0 )) {bcm_can_tx (op,NULL ); -if (!op->count && (op->flags &TX_COUNTEVT )) { +/* snapshot variables under lock to avoid torn reads racing +* with concurrent bcm_tx_setup() updates +*/ +spin_lock_bh (&op->bcm_tx_lock); + flags = op->flags; + count = op->count; + ival1 = op->ival1; + ival2 = op->ival2; +spin_unlock_bh (&op->bcm_tx_lock); + +if (!count && (flags &TX_COUNTEVT )) {/* create notification to user */ memset (&msg_head,0 ,sizeof (msg_head)); msg_head.opcode =TX_EXPIRED ; - msg_head.flags = op->flags; - msg_head.count = op->count; - msg_head.ival1 = op->ival1; - msg_head.ival2 = op->ival2; + msg_head.flags = flags; + msg_head.count = count; + msg_head.ival1 = ival1; + msg_head.ival2 = ival2; msg_head.can_id = op->can_id; msg_head.nframes =0 ;bcm_send_to_user (op, &msg_head,NULL ,0 ); } - }else if (op->kt_ival2) { + }else if (tx_ival2) {bcm_can_tx (op,NULL ); }
@@ -983,6 +1011,8 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,/* check the given can_id */ op =bcm_find_op (&bo->tx_ops, msg_head, ifindex);if (op) { +void *new_frames; +/* update existing BCM operation */ /*
@@ -993,11 +1023,23 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,if (msg_head->nframes > op->nframes)return -E2BIG ; -/* update CAN frames content */ +/* get new CAN frames content into a staging buffer before +* locking: validate and normalize the frames there so that +* bcm_can_tx() / bcm_tx_timeout_handler() never observe a +* partially updated or unvalidated frame in op->frames +*/ + new_frames =kmalloc (msg_head->nframes * op->cfsiz,GFP_KERNEL ); +if (!new_frames) +return -ENOMEM ; +for (i =0 ; i < msg_head->nframes; i++) { - cf = op->frames + op->cfsiz * i; + cf = new_frames + op->cfsiz * i; err =memcpy_from_msg ((u8 *)cf, msg, op->cfsiz); +if (err <0 ) { +kfree (new_frames); +return err; + }if (op->flags &CAN_FD_FRAME ) {if (cf->len >64 )
@@ -1007,36 +1049,38 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = -EINVAL ; } -if (err <0 ) +if (err <0 ) { +kfree (new_frames);return err; + }if (msg_head->flags &TX_CP_CAN_ID ) {/* copy can_id into frame */ cf->can_id = msg_head->can_id; } } + +spin_lock_bh (&op->bcm_tx_lock); + +/* update CAN frames content */ +memcpy (op->frames, new_frames, msg_head->nframes * op->cfsiz); + op->flags = msg_head->flags; -/* only lock for unlikely count/nframes/currframe changes */ if (op->nframes != msg_head->nframes || - op->flags &TX_RESET_MULTI_IDX || - op->flags &SETTIMER ) { - -spin_lock_bh (&op->bcm_tx_lock); + op->flags &TX_RESET_MULTI_IDX ) { +/* potentially update changed nframes */ + op->nframes = msg_head->nframes; +/* restart multiple frame transmission */ + op->currframe =0 ; + } -if (op->nframes != msg_head->nframes || - op->flags &TX_RESET_MULTI_IDX ) { -/* potentially update changed nframes */ - op->nframes = msg_head->nframes; -/* restart multiple frame transmission */ - op->currframe =0 ; - } +if (op->flags &SETTIMER ) + op->count = msg_head->count; -if (op->flags &SETTIMER ) - op->count = msg_head->count; +spin_unlock_bh (&op->bcm_tx_lock); -spin_unlock_bh (&op->bcm_tx_lock); - } +kfree (new_frames); }else {/* insert new BCM operation for the given can_id */
@@ -1113,10 +1157,12 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,if (op->flags &SETTIMER ) {/* set timer values */ +spin_lock_bh (&op->bcm_tx_lock); op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2; op->kt_ival1 =bcm_timeval_to_ktime (msg_head->ival1); op->kt_ival2 =bcm_timeval_to_ktime (msg_head->ival2); +spin_unlock_bh (&op->bcm_tx_lock);/* disable an active timer due to zero values? */ if (!op->kt_ival1 && !op->kt_ival2)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 138/456 can: bcm: validate frame length in bcm_rx_setup() for RTR replies
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 62ec41f364648be79d54d94d0d240ee326948afd upstream.
bcm_tx_setup() validates cf->len against the CAN/CAN FD DLC limits before installing frames for TX_SETUP, but bcm_rx_setup() never did the same for the RTR-reply frame configured via RX_SETUP with RX_RTR_FRAME.
--- net/can/bcm.c | 59 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 18 deletions(-)
net/can/bcm.c +41 -18
@@ -1194,22 +1194,37 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,return err; } -static void bcm_rx_setup_rtr_check (struct bcm_msg_head *msg_head, -struct bcm_op *op,void *new_frames) +static int bcm_rx_setup_rtr_check (struct bcm_msg_head *msg_head, +struct bcm_op *op,void *new_frames) { +struct canfd_frame *frame0 = new_frames; + +if (!(msg_head->flags &RX_RTR_FRAME )) +return 0 ; + +/* this frame is sent out as-is by bcm_can_tx() whenever a matching +* remote request is received, so validate its length the same way +* bcm_tx_setup() validates TX_SETUP frames before installing it +*/ +if (msg_head->flags &CAN_FD_FRAME ) { +if (frame0->len >64 ) +return -EINVAL ; + }else { +if (frame0->len >8 ) +return -EINVAL ; + } +/* funny feature in RX(!)_SETUP only for RTR-mode: * copy can_id into frame BUT without RTR-flag to * prevent a full-load-loopback-test ... ;-] * normalize this on the staged buffer, before it is * ever installed into op->frames. */ -if (msg_head->flags &RX_RTR_FRAME ) { -struct canfd_frame *frame0 = new_frames; +if ((msg_head->flags &TX_CP_CAN_ID ) || + frame0->can_id == op->can_id) + frame0->can_id = op->can_id & ~CAN_RTR_FLAG ; -if ((msg_head->flags &TX_CP_CAN_ID ) || - frame0->can_id == op->can_id) - frame0->can_id = op->can_id & ~CAN_RTR_FLAG ; - } +return 0 ; }/*
@@ -1272,7 +1287,11 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,return err; } -bcm_rx_setup_rtr_check (msg_head, op, new_frames); + err =bcm_rx_setup_rtr_check (msg_head, op, new_frames); +if (err <0 ) { +kfree (new_frames); +return err; + } }spin_lock_bh (&op->bcm_rx_update_lock);
@@ -1345,16 +1364,12 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,if (msg_head->nframes) { err =memcpy_from_msg (op->frames, msg, msg_head->nframes * op->cfsiz); -if (err <0 ) { -if (op->frames != &op->sframe) -kfree (op->frames); -if (op->last_frames != &op->last_sframe) -kfree (op->last_frames); -kfree (op); -return err; - } +if (err <0 ) + goto free_op; -bcm_rx_setup_rtr_check (msg_head, op, op->frames); + err =bcm_rx_setup_rtr_check (msg_head, op, op->frames); +if (err <0 ) + goto free_op; }/* bcm_can_tx / bcm_tx_timeout_handler needs this */
@@ -1456,6 +1471,14 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, }return msg_head->nframes * op->cfsiz +MHSIZ ; + +free_op: +if (op->frames != &op->sframe) +kfree (op->frames); +if (op->last_frames != &op->last_sframe) +kfree (op->last_frames); +kfree (op); +return err; }/*
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 139/456 can: bcm: add missing device refcount for CAN filter removal
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit d59948293ea34b6337ce2b5febab8510de70048c upstream.
sashiko-bot remarked a problem with a concurrent device unregistration in isotp.c which also is present in the bcm.c code. A former fix for raw.c commit c275a176e4b6 ("can: raw: add missing refcount for memory leak fix") introduced a netdevice_tracker which solves the issue for bcm.c too.
bcm_release(), bcm_delete_rx_op() and bcm_notifier() relied on dev_get_by_index(ifindex) to re-find the device for an rx_op before unregistering its filter. If a concurrent NETDEV_UNREGISTER has already unlisted the device from the ifindex table, that lookup fails and can_rx_unregister() is silently skipped, leaving a stale CAN filter pointing at the soon-to-be-freed bcm_op/socket.
Hold a netdev_hold()/netdev_put() tracked reference on op->rx_reg_dev from the moment the rx filter is registered in bcm_rx_setup() until it is unregistered in bcm_rx_unreg(), and use that reference directly in bcm_release() and bcm_delete_rx_op() instead of re-looking the device up by ifindex.
--- net/can/bcm.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-)
net/can/bcm.c +24 -20
@@ -884,6 +884,7 @@ static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)/* mark as removed subscription */ op->rx_reg_dev =NULL ; +dev_put (dev); }else printk (KERN_ERR "can-bcm: bcm_rx_unreg: registered device " "mismatch %p %p\n" , op->rx_reg_dev, dev);
@@ -914,17 +915,14 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh,* Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier() +* +* op->rx_reg_dev is a tracked reference taken +* when the subscription was registered, so it +* stays valid here even if a concurrent +* NETDEV_UNREGISTER already unlisted the dev. */ -if (op->rx_reg_dev) { -struct net_device *dev; - - dev =dev_get_by_index (sock_net (op->sk), - op->ifindex); -if (dev) { -bcm_rx_unreg (dev, op); -dev_put (dev); - } - } +if (op->rx_reg_dev) +bcm_rx_unreg (op->rx_reg_dev, op); }else can_rx_unregister(sock_net(op->sk), NULL, op->can_id,
@@ -1447,7 +1445,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, bcm_rx_handler, op, "bcm", sk); - op->rx_reg_dev = dev; +/* keep a reference so that a later +* unregister can safely reach the device even +* if a concurrent NETDEV_UNREGISTER has +* already unlisted it by ifindex +*/ +if (!err) { + op->rx_reg_dev = dev; +dev_hold (dev); + }dev_put (dev); }else {/* the requested device is gone - do not
@@ -1820,16 +1826,14 @@ static int bcm_release(struct socket *sock)* Only remove subscriptions that had not * been removed due to NETDEV_UNREGISTER * in bcm_notifier() +* +* op->rx_reg_dev is a tracked reference taken +* when the subscription was registered, so it +* stays valid here even if a concurrent +* NETDEV_UNREGISTER already unlisted the device. */ -if (op->rx_reg_dev) { -struct net_device *dev; - - dev =dev_get_by_index (net, op->ifindex); -if (dev) { -bcm_rx_unreg (dev, op); -dev_put (dev); - } - } +if (op->rx_reg_dev) +bcm_rx_unreg (op->rx_reg_dev, op); }else can_rx_unregister(net, NULL, op->can_id, REGMASK(op->can_id),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 140/456 can: bcm: fix stale rx/tx ops after device removal
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 3b762c0d950383ab7a002686c9136b9aa55d2d70 upstream.
RX: an RX_SETUP update(!) for an existing op skipped can_rx_register() unconditionally, even when a concurrent NETDEV_UNREGISTER had already torn down its registration (op->rx_reg_dev == NULL). This silently did not re-enable frame delivery for that updated filter. bcm_rx_setup() now re-registers in that case, while leaving rx_ops with ifindex = 0 (all CAN devices) which never carry a tracked rx_reg_dev registered as-is.
TX: bcm_notify() only handled bo->rx_ops on NETDEV_UNREGISTER, leaving tx_ops with an active cyclic transmission re-arming its hrtimer indefinitely to execute bcm_tx_timeout_handler(). Cancelling the hrtimer prevents the runaway timer and any injection into a later reused ifindex, since nothing else calls bcm_can_tx() for the op until an explicit TX_SETUP update re-arms it.
Unlike bcm_rx_unreg(), which clears the tracked rx_reg_dev for rx_ops, the ifindex is intentionally left unchanged for tx_ops. bcm_tx_setup() always rejects ifindex 0, so clearing it would strand the op: neither a later TX_SETUP (bcm_find_op()) nor TX_DELETE (bcm_delete_tx_op()) could ever find it again, since both require an exact ifindex match.
--- net/can/bcm.c | 54 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 10 deletions(-)
net/can/bcm.c +44 -10
@@ -1234,6 +1234,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,struct bcm_sock *bo =bcm_sk (sk);struct bcm_op *op;int do_rx_register; +int new_op =0 ;int err =0 ;if ((msg_head->flags &RX_FILTER_ID ) || (!(msg_head->nframes))) {
@@ -1318,8 +1319,15 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,/* free temporary frames / kfree(NULL) is safe */ kfree (new_frames); -/* Only an update -> do not call can_rx_register() */ - do_rx_register =0 ; +/* Don't register a new CAN filter for the rx_op update unless +* a concurrent NETDEV_UNREGISTER notifier already tore down +* the previous registration. In this case the receiver needs +* to be re-registered here so that this update doesn't +* silently stop delivering frames for the given ifindex. +* Ops with ifindex = 0 (all CAN interfaces) never carry a +* tracked rx_reg_dev and stay registered as-is. +*/ + do_rx_register = (ifindex && !op->rx_reg_dev) ?1 :0 ; }else {/* insert new BCM operation for the given can_id */
@@ -1389,6 +1397,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,/* call can_rx_register() */ do_rx_register =1 ; + new_op =1 ; }/* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
@@ -1402,7 +1411,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,if (op->flags &SETTIMER ) {/* set timers (locked) for newly created op */ -if (do_rx_register) { +if (new_op) {spin_lock_bh (&op->bcm_rx_update_lock); op->ival1 = msg_head->ival1; op->ival2 = msg_head->ival2;
@@ -1432,7 +1441,10 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, HRTIMER_MODE_REL_SOFT); } -/* now we can register for can_ids, if we added a new bcm_op */ +/* now we can register for can_ids, if we added a new bcm_op +* or need to re-register after a NETDEV_UNREGISTER tore down +* the previous registration of an existing op +*/ if (do_rx_register) {if (ifindex) {struct net_device *dev;
@@ -1462,18 +1474,32 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, err = -ENODEV ; } - }else + }else { err =can_rx_register (sock_net (sk),NULL , op->can_id,REGMASK (op->can_id), bcm_rx_handler, op,"bcm" , sk); + } +if (err) { -/* this bcm rx op is broken -> remove it */ -bcm_remove_op (op); +/* newly created bcm rx op is broken -> remove it */ +if (new_op) { +bcm_remove_op (op); +return err; + } + +/* an existing op just stays unregistered. +* Cancel op->timer and (defensively) op->thrtimer. +* Other settings can't be reached until the next +* successful RX_SETUP. +*/ +hrtimer_cancel (&op->timer); +hrtimer_cancel (&op->thrtimer);return err; } -/* add this bcm_op to the list of the rx_ops */ -list_add_rcu (&op->list, &bo->rx_ops); +/* add a new bcm_op to the list of the rx_ops */ +if (new_op) +list_add_rcu (&op->list, &bo->rx_ops); }return msg_head->nframes * op->cfsiz +MHSIZ ;
@@ -1689,11 +1715,19 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,case NETDEV_UNREGISTER :lock_sock (sk); -/* remove device specific receive entries */ +/* rx_ops: remove device specific receive entries */ list_for_each_entry (op, &bo->rx_ops, list)if (op->rx_reg_dev == dev)bcm_rx_unreg (dev, op); +/* tx_ops: stop device specific cyclic transmissions on the +* vanishing ifindex. Cancelling the timer is enough to stop +* cyclic bcm_can_tx() calls as there is no re-arming. +*/ +list_for_each_entry (op, &bo->tx_ops, list) +if (op->ifindex == dev->ifindex) +hrtimer_cancel (&op->timer); +/* remove device reference, if this is our bound device */ if (bo->bound && bo->ifindex == dev->ifindex) {#if IS_ENABLED (CONFIG_PROC_FS )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 141/456 can: bcm: fix data race on rx_stamp/rx_ifindex in bcm_rx_handler()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 58fd6cbc8541216af1d7ed272ea7ac2b66d50fd8 upstream.
For an rx op subscribed on all interfaces (ifindex == 0), the same op is registered once in the shared per-netns wildcard filter list, so bcm_rx_handler() can run concurrently on different CPUs for frames arriving on different net devices.
op->rx_stamp and op->rx_ifindex were written before bcm_rx_update_lock was taken, allowing concurrent writers to race each other - including a torn store of the 64-bit rx_stamp on 32-bit platforms.
Beyond a torn store bcm_send_to_user() must report the timestamp/ifindex of the very same frame whose content it is delivering. So the assignment is placed in the same unbroken bcm_rx_update_lock section as the content comparison.
As a side effect, the RTR-request frame feature (which never reach bcm_send_to_user()) no longer updates rx_stamp/rx_ifindex, since only the notification path needs them.
--- net/can/bcm.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
net/can/bcm.c +8 -5
@@ -756,11 +756,6 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)/* disable timeout */ hrtimer_cancel (&op->timer); -/* save rx timestamp */ - op->rx_stamp = skb->tstamp; -/* save originator for recvfrom() */ - op->rx_ifindex = skb->dev->ifindex; -/* op->flags/op->frames may be updated concurrently by bcm_rx_setup() */ spin_lock_bh (&op->bcm_rx_update_lock);
@@ -784,6 +779,14 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)*/ bcm_update_rx_stats (op); +/* save rx timestamp and originator for recvfrom() under lock. +* For an op subscribed on all interfaces (ifindex == 0) +* bcm_rx_handler() can run concurrently on different CPUs so +* the CAN content and the meta data must be bundled correctly. +*/ + op->rx_stamp = skb->tstamp; + op->rx_ifindex = skb->dev->ifindex; +if (op->flags &RX_FILTER_ID ) {/* the easiest case */ bcm_rx_update_and_send (op, op->last_frames, rxframe);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 142/456 can: bcm: track a single source interface for ANYDEV timeout/throttle ops
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 2f5976f54a04e9f18b25283036ac3136be453b17 upstream.
An ANYDEV rx op (ifindex == 0) with an active RX timeout and/or throttle timer has no defined semantics when matching frames arrive from several interfaces: bcm_rx_handler() can run concurrently for the same op on different CPUs, racing hrtimer_cancel()/ bcm_rx_starttimer() against bcm_rx_timeout_handler() and causing spurious RX_TIMEOUT notifications and last_frames corruption. The same concurrency lets throttled multiplex frames from different interfaces clobber the single rx_ifindex/rx_stamp fields shared by the op.
Add op->if_detected to track the first interface that delivers a matching frame while a timeout/throttle timer is configured, and reject frames from any other interface for that op. The claim is decided in bcm_rx_handler() before hrtimer_cancel() touches op->timer, so a rejected frame can never disturb the claimed interface's watchdog. RTR-mode ops are excluded via RX_RTR_FRAME, independent of kt_ival1/kt_ival2, since those may briefly hold a stale value from an earlier non-RTR configuration.
The claim is released in bcm_notify() on NETDEV_UNREGISTER and in bcm_rx_setup() when SETTIMER reconfigures the timer values.
A (re-)claim is only possible on CAN devices in NETREG_REGISTERED dev->reg_state to cover the release in bcm_notify() where reg_state becomes NETREG_UNREGISTERING until synchronize_net().
--- net/can/bcm.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-)
net/can/bcm.c +44 -5
@@ -114,6 +114,7 @@ struct bcm_op {struct hrtimer timer, thrtimer;ktime_t rx_stamp, kt_ival1, kt_ival2, kt_lastmsg;int rx_ifindex; +int if_detected;/* first received ifindex in ANYDEV rx_op mode */ int cfsiz;u32 count;u32 nframes;
@@ -753,6 +754,33 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)if (skb->len != op->cfsiz)return ; +/* An ANYDEV op with an active RX timeout and/or throttle timer +* tracks a single source interface: claim the first interface that +* delivers a matching frame and reject frames from any other one, +* before hrtimer_cancel() below can touch op->timer - this avoids +* racing bcm_rx_timeout_handler() across concurrent interfaces. +* RX_RTR_FRAME ops are excluded, as kt_ival1/kt_ival2 may briefly +* hold a stale value from an earlier non-RTR configuration. +*/ +if (!op->ifindex) { +spin_lock_bh (&op->bcm_rx_update_lock); + +if (!(op->flags &RX_RTR_FRAME ) && + (op->kt_ival1 || op->kt_ival2)) { +/* don't claim to vanishing interface */ +if (!op->if_detected && + skb->dev->reg_state ==NETREG_REGISTERED ) + op->if_detected = skb->dev->ifindex; + +if (op->if_detected != skb->dev->ifindex) { +spin_unlock_bh (&op->bcm_rx_update_lock); +return ; + } + } + +spin_unlock_bh (&op->bcm_rx_update_lock); + } +/* disable timeout */ hrtimer_cancel (&op->timer);
@@ -779,10 +807,9 @@ static void bcm_rx_handler(struct sk_buff *skb, void *data)*/ bcm_update_rx_stats (op); -/* save rx timestamp and originator for recvfrom() under lock. -* For an op subscribed on all interfaces (ifindex == 0) -* bcm_rx_handler() can run concurrently on different CPUs so -* the CAN content and the meta data must be bundled correctly. +/* save rx timestamp and originator for recvfrom() under lock: an +* ANYDEV op without an active timer can still run concurrently on +* different CPUs, so content and meta data must be bundled here. */ op->rx_stamp = skb->tstamp; op->rx_ifindex = skb->dev->ifindex;
@@ -1316,6 +1343,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->kt_ival1 =bcm_timeval_to_ktime (msg_head->ival1); op->kt_ival2 =bcm_timeval_to_ktime (msg_head->ival2); op->kt_lastmsg =0 ; + op->if_detected =0 ;/* reclaim ifindex in ANYDEV mode */ }spin_unlock_bh (&op->bcm_rx_update_lock);
@@ -1719,10 +1747,21 @@ static void bcm_notify(struct bcm_sock *bo, unsigned long msg,lock_sock (sk);/* rx_ops: remove device specific receive entries */ -list_for_each_entry (op, &bo->rx_ops, list) +list_for_each_entry (op, &bo->rx_ops, list) {if (op->rx_reg_dev == dev)bcm_rx_unreg (dev, op); +/* release an ANYDEV op's claim (see bcm_rx_handler()) +* on this now confirmed-gone interface. +*/ +if (!op->ifindex) { +spin_lock_bh (&op->bcm_rx_update_lock); +if (op->if_detected == dev->ifindex) + op->if_detected =0 ; +spin_unlock_bh (&op->bcm_rx_update_lock); + } + } +/* tx_ops: stop device specific cyclic transmissions on the * vanishing ifindex. Cancelling the timer is enough to stop * cyclic bcm_can_tx() calls as there is no re-arming.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 143/456 drm/rockchip: cdn-dp: add missing check in cdn_dp_config_video()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Shtylyov <[email protected]>
commit 46c31e1604d121221167cb09380de8c7d53290b9 upstream.
The result of cdn_dp_reg_write() is checked everywhere (with the error being logged by the callers) except one place in cdn_dp_config_video(). Add the missing result check, bailing out early on error...
Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool.
--- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/rockchip/cdn-dp-reg.c +2 -0
@@ -683,6 +683,8 @@ int cdn_dp_config_video(struct cdn_dp_de val =div_u64 (8 * (symbol +1 ), bit_per_pix) - val; val +=2 ; ret =cdn_dp_reg_write (dp,DP_VC_TABLE (15 ), val); +if (ret) + goto err_config_video;switch (video->color_depth) {case 6 :
Greg Kroah-Hartman
maintainerPATCH 144/456 drm/nouveau/acr: fix missing nvkm_done() in error path of nvkm_acr_oneinit()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit c3027973f692077a1b66a9fb26d6a7c46c0dc72c upstream.
In nvkm_acr_oneinit(), nvkm_kmap(acr->wpr) is invoked unconditionally at line 309 to obtain a mapping reference. Additionally, when both acr->wpr_fw and acr->wpr_comp are present, a second nvkm_kmap() is called inside the conditional block. Both mappings are expected to be released by nvkm_done(acr->wpr) at line 320 before the function returns successfully.
However, when a mismatch is detected during the loop within the conditional block, the function returns -EINVAL at line 318 without calling nvkm_done(). This results in a leak of the kmap reference(s) acquired earlier.
Fix the issue by invoking nvkm_done(acr->wpr) prior to the early return to ensure proper release of the mapping references.
--- drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c | 1 + 1 file changed, 1 insertion(+)
drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c +1 -0
@@ -314,6 +314,7 @@ nvkm_acr_oneinit(struct nvkm_subdev *sub i, us, fw); } } +nvkm_done (acr->wpr);return -EINVAL ; }nvkm_done (acr->wpr);
Greg Kroah-Hartman
maintainerPATCH 145/456 drm/radeon: fix r100_copy_blit for large BOs
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pavel Ondračka <[email protected]>
commit f896e86273dbbebb5eac966b4a201b5c62a02e9a upstream.
r100_copy_blit() copies BOs as 1024-pixel-wide ARGB8888 blits, so one GPU page becomes one blit row. Large copies are split into chunks of at most 8191 rows.
The kernel register header names the packet coordinate dwords SRC_Y_X and DST_Y_X. In the BITBLT_MULTI description in R5xx_Acceleration_v1.5.pdf docs, these correspond to [SRC_X1 | SRC_Y1] and [DST_X1 | DST_Y1], which are signed 13-bit coordinates in the -8192..8191 range. The old code kept SRC/DST_PITCH_OFFSET at the BO base and used SRC_Y_X/DST_Y_X as the chunk address, so large BO moves could exceed that coordinate range.
Compute per-chunk SRC/DST_PITCH_OFFSET bases and emit zero source and destination coordinates. r100_copy_blit() already packs SRC/DST_PITCH_OFFSET as pitch plus base offset, so large chunk addresses belong there rather than in the coordinate fields.
This fixes Prison Architect corruption with 4096x4096 mipped textures after they are evicted to GTT under memory pressure on RV530.
--- drivers/gpu/drm/radeon/r100.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/gpu/drm/radeon/r100.c +9 -4
@@ -904,6 +904,7 @@ struct radeon_fence *r100_copy_blit(stru {struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX ];struct radeon_fence *fence; +uint64_t cur_src_offset, cur_dst_offset;uint32_t cur_pages;uint32_t stride_bytes =RADEON_GPU_PAGE_SIZE ;uint32_t pitch;
@@ -932,6 +933,10 @@ struct radeon_fence *r100_copy_blit(stru cur_pages =8191 ; } num_gpu_pages -= cur_pages; + cur_src_offset = src_offset + + (uint64_t )num_gpu_pages *RADEON_GPU_PAGE_SIZE ; + cur_dst_offset = dst_offset + + (uint64_t )num_gpu_pages *RADEON_GPU_PAGE_SIZE ;/* pages are in Y direction - height page width in X direction - width */
@@ -948,13 +953,13 @@ struct radeon_fence *r100_copy_blit(struRADEON_DP_SRC_SOURCE_MEMORY |RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); -radeon_ring_write (ring, (pitch <<22 ) | (src_offset >>10 )); -radeon_ring_write (ring, (pitch <<22 ) | (dst_offset >>10 )); +radeon_ring_write (ring, (pitch <<22 ) | (cur_src_offset >>10 )); +radeon_ring_write (ring, (pitch <<22 ) | (cur_dst_offset >>10 ));radeon_ring_write (ring, (0x1fff ) | (0x1fff <<16 ));radeon_ring_write (ring,0 );radeon_ring_write (ring, (0x1fff ) | (0x1fff <<16 )); -radeon_ring_write (ring, num_gpu_pages); -radeon_ring_write (ring, num_gpu_pages); +radeon_ring_write (ring,0 ); +radeon_ring_write (ring,0 );radeon_ring_write (ring, cur_pages | (stride_pixels <<16 )); }radeon_ring_write (ring,PACKET0 (RADEON_DSTCACHE_CTLSTAT ,0 ));
Greg Kroah-Hartman
maintainerPATCH 146/456 drm/amdgpu/sdma5.2: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit b9dd618a635d39fbb211454b6e8837b2a7f10fb0 upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +2 -2
@@ -449,7 +449,7 @@ static void sdma_v5_2_ring_emit_fence(stamdgpu_ring_write (ring,SDMA_PKT_HEADER_OP (SDMA_OP_FENCE ) |SDMA_PKT_FENCE_HEADER_MTYPE (0x3 ));/* Ucached(UC) */ /* zero in first two bits */ -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
@@ -460,7 +460,7 @@ static void sdma_v5_2_ring_emit_fence(stamdgpu_ring_write (ring,SDMA_PKT_HEADER_OP (SDMA_OP_FENCE ) |SDMA_PKT_FENCE_HEADER_MTYPE (0x3 ));/* zero in first two bits */ -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (seq));
Greg Kroah-Hartman
maintainerPATCH 147/456 drm/amdgpu/sdma5.0: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 9e98ed3113943257ad6e5c1e6beddbdb482a70ad upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +2 -2
@@ -552,7 +552,7 @@ static void sdma_v5_0_ring_emit_fence(stamdgpu_ring_write (ring,SDMA_PKT_HEADER_OP (SDMA_OP_FENCE ) |SDMA_PKT_FENCE_HEADER_MTYPE (0x3 ));/* Ucached(UC) */ /* zero in first two bits */ -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
@@ -563,7 +563,7 @@ static void sdma_v5_0_ring_emit_fence(stamdgpu_ring_write (ring,SDMA_PKT_HEADER_OP (SDMA_OP_FENCE ) |SDMA_PKT_FENCE_HEADER_MTYPE (0x3 ));/* zero in first two bits */ -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (seq));
Greg Kroah-Hartman
maintainerPATCH 148/456 drm/i915: Return NULL on error in active_instance
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joonas Lahtinen <[email protected]>
commit 1e33f0de5fdcd09e51fdec1e5822448970b6420f upstream.
Avoid returning &node->base when node is NULL due to OOM during GFP_ATOMIC allocation.
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/i915_active.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/gpu/drm/i915/i915_active.c +6 -1
@@ -319,7 +319,7 @@ active_instance(struct i915_active *ref,*/ node =kmem_cache_alloc (slab_cache,GFP_ATOMIC );if (!node) - goto out; + goto err;__i915_active_fence_init (&node->base,NULL , node_retire); node->ref = ref;
@@ -333,6 +333,11 @@ out:spin_unlock_irq (&ref->tree_lock);return &node->base; + +err: +spin_unlock_irq (&ref->tree_lock); + +return NULL ; } void __i915_active_init(struct i915_active *ref,
Greg Kroah-Hartman
maintainerPATCH 149/456 drm/i915/gem: Do not leak siblings[] on proto context error
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joonas Lahtinen <[email protected]>
commit eed3de2acf6aa5154d49098b026710b646db67ee upstream.
After a successful BALANCE/PARALLEL_SUBMIT extension on context creation, error during processing of next user extension leaks the siblings[] array. Fix that.
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/gem/i915_gem_context.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-)
drivers/gpu/drm/i915/gem/i915_gem_context.c +14 -8
@@ -565,8 +565,8 @@ static int set_proto_ctx_engines(structstruct intel_engine_cs *engine;if (copy_from_user (&ci, &user->engines[n],sizeof (ci))) { -kfree (set.engines); -return -EFAULT ; + err = -EFAULT ; + goto err; }memset (&set.engines[n],0 ,sizeof (set.engines[n]));
@@ -582,8 +582,8 @@ static int set_proto_ctx_engines(structdrm_dbg (&i915->drm,"Invalid engine[%d]: { class:%d, instance:%d }\n" , n, ci.engine_class, ci.engine_instance); -kfree (set.engines); -return -ENOENT ; + err = -ENOENT ; + goto err; } set.engines[n].type =I915_GEM_ENGINE_TYPE_PHYSICAL ;
@@ -596,15 +596,21 @@ static int set_proto_ctx_engines(struct set_proto_ctx_engines_extensions,ARRAY_SIZE (set_proto_ctx_engines_extensions), &set); -if (err) { -kfree (set.engines); -return err; - } +if (err) + goto err_extensions; pc->num_user_engines = set.num_engines; pc->user_engines = set.engines;return 0 ; + +err_extensions: +for (n =0 ; n < set.num_engines; n++) +kfree (set.engines[n].siblings); +err: +kfree (set.engines); + +return err; } static int set_proto_ctx_sseu(struct drm_i915_file_private *fpriv,
Greg Kroah-Hartman
maintainerPATCH 150/456 drm/i915/gem: Fix NULL deref in I915_CONTEXT_PARAM_SSEU
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joonas Lahtinen <[email protected]>
commit 2b56757a9a7456825eb668fde92299e01c5e2721 upstream.
Setting context engine slot N into I915_ENGINE_CLASS_INVALID / I915_ENGINE_CLASS_INVALID_NONE and attempting to apply I915_CONTEXT_PARAM_SSEU to the same slot N will deref NULL. Fix that.
Discovered using AI-assisted static analysis confirmed by Intel Product Security.
--- drivers/gpu/drm/i915/gem/i915_gem_context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/gpu/drm/i915/gem/i915_gem_context.c +1 -1
@@ -652,7 +652,7 @@ static int set_proto_ctx_sseu(struct drm pe = &pc->user_engines[idx];/* Only render engine supports RPCS configuration. */ -if (pe->engine->class !=RENDER_CLASS ) +if (!pe->engine || pe->engine->class !=RENDER_CLASS )return -EINVAL ; sseu = &pe->sseu;
Greg Kroah-Hartman
maintainerPATCH 151/456 drm/amdgpu: Fix VFCT bus number matching with soft filter
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mario Limonciello <[email protected]>
commit db7e8108809a2245f0a17ba323f027cac0941ffb upstream.
On systems where PCI bus renumbering occurs (e.g. pci=realloc, resource conflicts), the runtime bus number may differ from the BIOS POST bus number recorded in the VFCT table. This causes amdgpu_acpi_vfct_bios() to fail finding the VBIOS even though the correct device entry exists.
Introduce amdgpu_acpi_vfct_match() which treats the bus number as a soft filter: vendor/device/function identity is the hard requirement, while exact bus match is the preferred path. When bus numbers disagree but device identity matches, accept the VFCT entry and log a dev_notice for diagnostics.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c | 45 +++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +40 -5
@@ -363,6 +363,45 @@ static bool amdgpu_read_disabled_bios(st }#ifdef CONFIG_ACPI +/** +* amdgpu_acpi_vfct_match() - Check if a VFCT entry matches the device +* @adev: AMDGPU device +* @vhdr: VFCT image header to check +* +* VFCT entries contain the PCI bus number as recorded during BIOS POST. +* On systems where the kernel renumbers PCI buses (e.g. pci=realloc or +* resource conflicts), the runtime bus number may differ from the POST +* value. Match by device identity (vendor + device + function) and use +* the bus number as a preference: exact bus match is preferred, but when +* the bus numbers disagree we accept the entry if the device identity +* matches. +* +* Returns: 0 on match, -ENODEV on no match +*/ +static int amdgpu_acpi_vfct_match (struct amdgpu_device *adev, +VFCT_IMAGE_HEADER *vhdr) +{ +/* Vendor and device IDs must always match */ +if (vhdr->VendorID != adev->pdev->vendor || + vhdr->DeviceID != adev->pdev->device) +return -ENODEV ; + +if (vhdr->PCIDevice !=PCI_SLOT (adev->pdev->devfn) || + vhdr->PCIFunction !=PCI_FUNC (adev->pdev->devfn)) +return -ENODEV ; + +/* Exact bus number match - preferred */ +if (vhdr->PCIBus == adev->pdev->bus->number) +return 0 ; + +/* Bus mismatch but device identity matches (PCI renumbering case) */ +dev_notice (adev->dev, +"VFCT bus number mismatch: table %u != runtime %u, matching by device identity (vendor 0x%04x device 0x%04x)\n" , + vhdr->PCIBus, adev->pdev->bus->number, + adev->pdev->vendor, adev->pdev->device); +return 0 ; +} +static bool amdgpu_acpi_vfct_bios (struct amdgpu_device *adev) {struct acpi_table_header *hdr;
@@ -398,11 +437,7 @@ static bool amdgpu_acpi_vfct_bios(struct }if (vhdr->ImageLength && - vhdr->PCIBus == adev->pdev->bus->number && - vhdr->PCIDevice ==PCI_SLOT (adev->pdev->devfn) && - vhdr->PCIFunction ==PCI_FUNC (adev->pdev->devfn) && - vhdr->VendorID == adev->pdev->vendor && - vhdr->DeviceID == adev->pdev->device) { + !amdgpu_acpi_vfct_match (adev, vhdr)) { adev->bios =kmemdup (&vbios->VbiosContent, vhdr->ImageLength,GFP_KERNEL );
Greg Kroah-Hartman
maintainerPATCH 152/456 drm/amd/pm/ci: Dont disable MCLK DPM on Bonaire 0x6658 (R7 260X)
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Timur Kristóf <[email protected]>
commit 85371c5ef502d10add72eab38711e191dccea981 upstream.
The old radeon driver has a documented workaround in ci_dpm.c which claims that Bonaire 0x6658 with old memory controller firmware is unstable with MCLK DPM, so as a precaution I disabled MCLK DPM on this ASIC in amdgpu.
Note that the old MC firmware is not actually used with amdgpu, but in theory it's possible that the VBIOS sets up the ASIC with an old MC firmware that is already running when amdgpu initializes (in which case amdgpu doesn't load its own firmware).
What I expected to happen is that the GPU would simply use its maximum memory clock, and indeed this is what seemed to happen according to amdgpu_pm_info which reads the current MCLK value from the SMU.
However, some users reported a huge perf regression and upon a closer look it seems that the GPU seems to not actually use the highest MCLK value, despite the SMU reporting that it does.
Let's not disable MCLK DPM on Bonaire 0x6658 (R7 260X).
Keep MCLK DPM disabled on R9 M380 in the 2015 iMac because that still hangs if we enable it.
--- drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/pm/powerplay/hwmgr/hwmgr.c +2 -5
@@ -106,11 +106,8 @@ int hwmgr_early_init(struct pp_hwmgr *hw hwmgr->od_enabled = false;switch (hwmgr->chip_id) {case CHIP_BONAIRE : -/* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM -* R7 260X cards with old MC ucode: MCLK DPM is unstable -*/ -if (adev->pdev->subsystem_vendor ==0x106B || - adev->pdev->device ==0x6658 ) { +/* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM */ +if (adev->pdev->subsystem_vendor ==0x106B ) {dev_info (adev->dev,"disabling MCLK DPM on quirky ASIC" ); adev->pm.pp_feature &= ~PP_MCLK_DPM_MASK ; hwmgr->feature_mask &= ~PP_MCLK_DPM_MASK ;
Greg Kroah-Hartman
maintainerPATCH 153/456 drm/amdgpu: fix bo->pin leaking in amdgpu_bo_create_reserved
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhu Lingshan <[email protected]>
commit a2f895f3c852063258d62e9f74b081de07ca95df upstream.
amdgpu_bo_create_reserved() only allocates a new BO when *bo_ptr (struct amdgpu_bo **bo_ptr as input parameter) is NULL, it simply skips creation when *bo_ptr is non-NULL. But it unconditionally reserves, pins, gart allocates and maps the BO afterwards.
When the same non-NULL BO pointer is passed in again,
for example firmware buffers that live in adev and are re-loaded on every resume / cp_resume / start
under AMDGPU_FW_LOAD_DIRECT, amdgpu_bo_pin() just increases pin_count unconditionally, however the matching teardown only unpins once, so pin_count never drops to zero, so TTM is not able to move, swap or evict a BO, causing BO leaks.
This commit fixes this issue by only pinning the bo once at creation, and repeated calls no longer take additional pin references.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +8 -5
@@ -266,10 +266,12 @@ int amdgpu_bo_create_reserved(struct amd goto error_free; } - r =amdgpu_bo_pin (*bo_ptr, domain); -if (r) { -dev_err (adev->dev,"(%d) kernel bo pin failed\n" , r); - goto error_unreserve; +if (free) { + r =amdgpu_bo_pin (*bo_ptr, domain); +if (r) { +dev_err (adev->dev,"(%d) kernel bo pin failed\n" , r); + goto error_unreserve; + } } r =amdgpu_ttm_alloc_gart (&(*bo_ptr)->tbo);
@@ -292,7 +294,8 @@ int amdgpu_bo_create_reserved(struct amdreturn 0 ; error_unpin: -amdgpu_bo_unpin (*bo_ptr); +if (free) +amdgpu_bo_unpin (*bo_ptr); error_unreserve:amdgpu_bo_unreserve (*bo_ptr);
Greg Kroah-Hartman
maintainerPATCH 154/456 drm/vmwgfx: Validate vmw_surface_metadata::array_size
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ian Forbes <[email protected]>
commit a4f55260f7f7d4dc4d0ee55063dfb0c457b77991 upstream.
This field comes from userspace and should be validated against specific limits depending on which Shader Model (SM) is available.
--- drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +17 -5
@@ -104,7 +104,7 @@ static int vmw_gb_surface_unbind(structstatic int vmw_gb_surface_destroy (struct vmw_resource *res);static int vmw_gb_surface_define_internal (struct drm_device *dev, -struct drm_vmw_gb_surface_create_ext_req *req, +const struct drm_vmw_gb_surface_create_ext_req *req,struct drm_vmw_gb_surface_create_rep *rep,struct drm_file *file_priv); static int
@@ -1447,7 +1447,7 @@ int vmw_gb_surface_reference_ext_ioctl(s*/ static int vmw_gb_surface_define_internal (struct drm_device *dev, -struct drm_vmw_gb_surface_create_ext_req *req, +const struct drm_vmw_gb_surface_create_ext_req *req,struct drm_vmw_gb_surface_create_rep *rep,struct drm_file *file_priv) {
@@ -1466,9 +1466,21 @@ vmw_gb_surface_define_internal(struct dr req->base.svga3d_flags);/* array_size must be null for non-GL3 host. */ -if (req->base.array_size >0 && !has_sm4_context (dev_priv)) { -VMW_DEBUG_USER ("SM4 surface not supported.\n" ); -return -EINVAL ; +if (req->base.array_size >0 ) { +if (has_sm5_context (dev_priv)) { +if (req->base.array_size >SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE ) { +VMW_DEBUG_USER ("Invalid Surface Array Size.\n" ); +return -EINVAL ; + } + }else if (has_sm4_context (dev_priv)) { +if (req->base.array_size >SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE ) { +VMW_DEBUG_USER ("Invalid Surface Array Size.\n" ); +return -EINVAL ; + } + }else { +VMW_DEBUG_USER ("SM4+ surface not supported.\n" ); +return -EINVAL ; + } }if (!has_sm4_1_context (dev_priv)) {
Greg Kroah-Hartman
maintainerPATCH 155/456 media: airspy: Return queued buffers on start_streaming() failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Valery Borovsky <[email protected]>
commit 04344d0b4929caa94c0df72f767752aa0935ef5d upstream.
The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.
airspy_start_streaming() returned -ENODEV early when the USB device had been disconnected (s->udev == NULL) without returning any buffers that buf_queue() had already accepted. Take v4l2_lock first and jump to the existing err_clear_bit label, which already drains s->queued_bufs via vb2_buffer_done(..., VB2_BUF_STATE_QUEUED) before unlocking.
This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").
--- drivers/media/usb/airspy/airspy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/media/usb/airspy/airspy.c +5 -3
@@ -518,11 +518,13 @@ static int airspy_start_streaming(structdev_dbg (s->dev,"\n" ); -if (!s->udev) -return -ENODEV ; -mutex_lock (&s->v4l2_lock); +if (!s->udev) { + ret = -ENODEV ; + goto err_clear_bit; + } + s->sequence =0 ;set_bit (POWER_ON , &s->flags);
Greg Kroah-Hartman
maintainerPATCH 156/456 media: cec: seco: unregister adapter on IR probe failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit c3a78691be8245e52ced489f268e413f18061ac2 upstream.
If secocec_ir_probe() fails after cec_register_adapter() succeeds, probe returns an error and the driver remove callback is not called. The current unwind path unregisters the notifier and then falls through to cec_delete_adapter(), which violates the CEC adapter lifetime rules after a successful registration.
Add a registered-adapter unwind path that unregisters the notifier and the adapter instead.
--- drivers/media/cec/platform/seco/seco-cec.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/media/cec/platform/seco/seco-cec.c +5 -1
@@ -662,7 +662,7 @@ static int secocec_probe(struct platform ret =secocec_ir_probe (secocec);if (ret) - goto err_notifier; + goto err_unregister_adapter;platform_set_drvdata (pdev, secocec);
@@ -670,6 +670,10 @@ static int secocec_probe(struct platformreturn ret; +err_unregister_adapter: +cec_notifier_cec_adap_unregister (secocec->notifier, secocec->cec_adap); +cec_unregister_adapter (secocec->cec_adap); + goto err; err_notifier:cec_notifier_cec_adap_unregister (secocec->notifier, secocec->cec_adap); err_delete_adapter:
Greg Kroah-Hartman
maintainerPATCH 157/456 media: cedrus: clean up media device on probe failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 2c869b6969f3061cbbdab587f4c0a88bd7fc3cc9 upstream.
cedrus_probe() initializes the media device before registering the video device, the media controller, and the media device. If any of those later steps fails, probe returns without calling media_device_cleanup(), so the media device internals initialized by media_device_init() are left behind.
Add a media-device cleanup label to the probe unwind path and route video registration failures through it as well.
--- drivers/staging/media/sunxi/cedrus/cedrus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/staging/media/sunxi/cedrus/cedrus.c +3 -2
@@ -422,7 +422,7 @@ static int cedrus_probe(struct platform_ ret =video_register_device (vfd,VFL_TYPE_VIDEO ,0 );if (ret) {v4l2_err (&dev->v4l2_dev,"Failed to register video device\n" ); - goto err_m2m; + goto err_media; } v4l2_info(&dev->v4l2_dev,
@@ -448,7 +448,8 @@ err_m2m_mc:v4l2_m2m_unregister_media_controller (dev->m2m_dev); err_video:video_unregister_device (&dev->vfd); -err_m2m: +err_media: +media_device_cleanup (&dev->mdev);v4l2_m2m_release (dev->m2m_dev); err_v4l2:v4l2_device_unregister (&dev->v4l2_dev);
Greg Kroah-Hartman
maintainerPATCH 158/456 media: cedrus: Fix missing cleanup in error path
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Samuel Holland <[email protected]>
commit d99732334aaf33b9f93926b70b6a11c2cef3de39 upstream.
According to the documentation struct v4l2_fh has to be cleaned up with v4l2_fh_exit() before being freed. [1]
Currently there is no actual bug here, when v4l2_fh_exit() isn't called. v4l2_fh_exit() in this case only destroys internal mutex. But it may change in the future, when v4l2_fh_init/v4l2_fh_exit will be enhanced.
1. https://docs.kernel.org/driver-api/media/v4l2-fh.html
--- drivers/staging/media/sunxi/cedrus/cedrus.c | 1 + 1 file changed, 1 insertion(+)
drivers/staging/media/sunxi/cedrus/cedrus.c +1 -0
@@ -302,6 +302,7 @@ static int cedrus_open(struct file *file err_ctrls:v4l2_ctrl_handler_free (&ctx->hdl); err_free: +v4l2_fh_exit (&ctx->fh);kfree (ctx);mutex_unlock (&dev->dev_mutex);
Greg Kroah-Hartman
maintainerPATCH 159/456 media: cedrus: skip invalid H.264 reference list entries
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
commit 10358ea986c3c85516d1c8206486464f79d36e76 upstream.
Cedrus consumes H.264 ref_pic_list0/ref_pic_list1 entries from the stateless slice control and later uses their indices to look up decode->dpb[] in _cedrus_write_ref_list().
Rejecting such controls in cedrus_try_ctrl() would break existing userspace, since stateless H.264 reference lists may legitimately carry out-of-range indices for missing references. Instead, guard the actual DPB lookup in Cedrus and skip entries whose indices do not fit the fixed V4L2_H264_NUM_DPB_ENTRIES array.
This keeps the fix local to the driver use site and avoids out-of-bounds reads from malformed or unsupported reference list entries.
--- drivers/staging/media/sunxi/cedrus/cedrus_h264.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/staging/media/sunxi/cedrus/cedrus_h264.c +3 -0
@@ -190,6 +190,9 @@ static void _cedrus_write_ref_list(strucu8 dpb_idx; dpb_idx = ref_list[i].index; +if (dpb_idx >=V4L2_H264_NUM_DPB_ENTRIES ) +continue ; + dpb = &decode->dpb[dpb_idx];if (!(dpb->flags &V4L2_H264_DPB_ENTRY_FLAG_ACTIVE ))
Greg Kroah-Hartman
maintainerPATCH 160/456 media: cx231xx: fix devres lifetime
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 7d6358ab02866e5b7ed8d3a00805297617bbb0ec upstream.
USB drivers bind to USB interfaces and any device managed resources should have their lifetime tied to the interface rather than parent USB device. This avoids issues like memory leaks when drivers are unbound without their devices being physically disconnected (e.g. on probe deferral or configuration changes).
Fix the driver state lifetime so that it is released on driver unbind.
--- drivers/media/usb/cx231xx/cx231xx-cards.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
drivers/media/usb/cx231xx/cx231xx-cards.c +11 -5
@@ -1577,7 +1577,8 @@ static int cx231xx_init_v4l2(struct cx23 dev->video_mode.end_point_addr, dev->video_mode.num_alt); - dev->video_mode.alt_max_pkt_size =devm_kmalloc_array (&udev->dev,32 , dev->video_mode.num_alt,GFP_KERNEL ); + dev->video_mode.alt_max_pkt_size =devm_kmalloc_array (&interface->dev,32 , + dev->video_mode.num_alt,GFP_KERNEL );if (dev->video_mode.alt_max_pkt_size ==NULL )return -ENOMEM ;
@@ -1618,7 +1619,8 @@ static int cx231xx_init_v4l2(struct cx23 dev->vbi_mode.num_alt);/* compute alternate max packet sizes for vbi */ - dev->vbi_mode.alt_max_pkt_size =devm_kmalloc_array (&udev->dev,32 , dev->vbi_mode.num_alt,GFP_KERNEL ); + dev->vbi_mode.alt_max_pkt_size =devm_kmalloc_array (&interface->dev,32 , + dev->vbi_mode.num_alt,GFP_KERNEL );if (dev->vbi_mode.alt_max_pkt_size ==NULL )return -ENOMEM ;
@@ -1660,7 +1662,9 @@ static int cx231xx_init_v4l2(struct cx23"sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n" , dev->sliced_cc_mode.end_point_addr, dev->sliced_cc_mode.num_alt); - dev->sliced_cc_mode.alt_max_pkt_size =devm_kmalloc_array (&udev->dev,32 , dev->sliced_cc_mode.num_alt,GFP_KERNEL ); + dev->sliced_cc_mode.alt_max_pkt_size =devm_kmalloc_array (&interface->dev,32 , + dev->sliced_cc_mode.num_alt, +GFP_KERNEL );if (dev->sliced_cc_mode.alt_max_pkt_size ==NULL )return -ENOMEM ;
@@ -1724,7 +1728,7 @@ static int cx231xx_usb_probe(struct usb_ udev =usb_get_dev (interface_to_usbdev (interface));/* allocate memory for our device state and initialize it */ - dev =devm_kzalloc (&udev->dev,sizeof (*dev),GFP_KERNEL ); + dev =devm_kzalloc (&interface->dev,sizeof (*dev),GFP_KERNEL );if (dev ==NULL ) { retval = -ENOMEM ; goto err_if;
@@ -1854,7 +1858,9 @@ static int cx231xx_usb_probe(struct usb_ dev->ts1_mode.end_point_addr, dev->ts1_mode.num_alt); - dev->ts1_mode.alt_max_pkt_size =devm_kmalloc_array (&udev->dev,32 , dev->ts1_mode.num_alt,GFP_KERNEL ); + dev->ts1_mode.alt_max_pkt_size =devm_kmalloc_array (&interface->dev,32 , + dev->ts1_mode.num_alt, +GFP_KERNEL );if (dev->ts1_mode.alt_max_pkt_size ==NULL ) { retval = -ENOMEM ; goto err_video_alt;
Greg Kroah-Hartman
maintainerPATCH 161/456 media: cx23885: add ioremap return check and cleanup
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wang Jun <[email protected]>
commit a0701e387b46e2481c05b47f1235b954bfc2af3e upstream.
Add a check for the return value of pci_ioremap_bar() in cx23885_dev_setup().
If ioremap for BAR0 fails, release the already allocated PCI memory region,
decrement the device count, and return -ENODEV.
This prevents a potential null pointer dereference and ensures proper cleanup
on memory mapping failure.
--- drivers/media/pci/cx23885/cx23885-core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
drivers/media/pci/cx23885/cx23885-core.c +12 -2
@@ -990,8 +990,12 @@ static int cx23885_dev_setup(struct cx23 }/* PCIe stuff */ - dev->lmmio =ioremap (pci_resource_start (dev->pci,0 ), -pci_resource_len (dev->pci,0 )); + dev->lmmio =pci_ioremap_bar (dev->pci,0 ); +if (!dev->lmmio) { +dev_err (&dev->pci->dev,"CORE %s: can't ioremap MMIO memory\n" , + dev->name); + goto err_release_region; + } dev->bmmio = (u8 __iomem *)dev->lmmio;
@@ -1096,6 +1100,12 @@ static int cx23885_dev_setup(struct cx23 }return 0 ; + +err_release_region: +release_mem_region (pci_resource_start (dev->pci,0 ), +pci_resource_len (dev->pci,0 )); + cx23885_devcount--; +return -ENODEV ; }static void cx23885_dev_unregister (struct cx23885_dev *dev)
Greg Kroah-Hartman
maintainerPATCH 162/456 media: meson: vdec: Fix memory leak in error path of vdec_open
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anand Moon <[email protected]>
commit 940f161f734b25f175a95d2684c2021f6323693a upstream.
The vdec_open() function previously jumped directly to err_m2m_release when vdec_init_ctrls() failed, skipping release of the m2m context. This caused a resource leak.
Fix it by introducing a proper err_m2m_ctx_release label that calls v4l2_m2m_ctx_release(sess->m2m_ctx) before releasing the m2m device.
This was identified via kmemleak:
unreferenced object 0xffff0000205d6878 (size 8):
comm "v4l_id", pid 5289, jiffies 4294938580
hex dump (first 8 bytes):
40 d2 49 18 00 00 ff ff @.I.....
backtrace (crc d3204599):
kmemleak_alloc+0xc8/0xf0
__kvmalloc_node_noprof+0x60c/0x850
v4l2_ctrl_handler_init_class+0x1b4/0x2e8 [videodev]
vdec_open+0x1f4/0x788 [meson_vdec]
v4l2_open+0x144/0x460 [videodev]
chrdev_open+0x1ac/0x500
do_dentry_open+0x3f0/0xfe8
vfs_open+0x68/0x320
do_open+0x2d8/0x9a8
path_openat+0x1d0/0x4f0
do_filp_open+0x190/0x380
do_sys_openat2+0xf8/0x1b0
__arm64_sys_openat+0x13c/0x1e8
invoke_syscall+0xdc/0x268
el0_svc_common.constprop.0+0x178/0x258
do_el0_svc+0x4c/0x70--- drivers/staging/media/meson/vdec/vdec.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/staging/media/meson/vdec/vdec.c +3 -1
@@ -897,7 +897,7 @@ static int vdec_open(struct file *file) ret =vdec_init_ctrls (sess);if (ret) - goto err_m2m_release; + goto err_m2m_ctx_release; sess->pixfmt_cap = formats[0 ].pixfmts_cap[0 ]; sess->fmt_out = &formats[0 ];
@@ -922,6 +922,8 @@ static int vdec_open(struct file *file)return 0 ; +err_m2m_ctx_release: +v4l2_m2m_ctx_release (sess->m2m_ctx); err_m2m_release:v4l2_m2m_release (sess->m2m_dev); err_free_sess:
Greg Kroah-Hartman
maintainerPATCH 163/456 media: msi2500: Return queued buffers on start_streaming() failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Valery Borovsky <[email protected]>
commit 7201c17786a498497bca57752883b90914d405ac upstream.
The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.
msi2500_start_streaming() had five error paths that all hit this trap and were further tangled by ret-overwriting between calls:
- -ENODEV when the USB device was already disconnected
- -ERESTARTSYS when mutex_lock_interruptible() was interrupted
- msi2500_set_usb_adc() failure: ret was silently overwritten by the next call (msi2500_isoc_init), so the error was lost entirely
- msi2500_isoc_init() failure: cleanup_queued_bufs was called, but the function then fell through to msi2500_ctrl_msg() and again masked the original error by overwriting ret
- msi2500_ctrl_msg(CMD_START_STREAMING) failure: no cleanup at all, leaving isoc URBs submitted with no way for the driver to consume them
Consolidate the error paths into a small goto chain. Every failure now stops the function, drains the queued-buffer list, and returns the real error code. The ctrl_msg failure path also rolls back the preceding msi2500_isoc_init() via msi2500_isoc_cleanup() before unlocking and draining.
The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics.
This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").
--- drivers/media/usb/msi2500/msi2500.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-)
drivers/media/usb/msi2500/msi2500.c +24 -8
@@ -541,7 +541,8 @@ static int msi2500_isoc_init(struct msi2 }/* Must be called with vb_queue_lock hold */ -static void msi2500_cleanup_queued_bufs (struct msi2500_dev *dev) +static void msi2500_cleanup_queued_bufs (struct msi2500_dev *dev, +enum vb2_buffer_state state) {unsigned long flags;
@@ -554,7 +555,7 @@ static void msi2500_cleanup_queued_bufs( buf =list_entry (dev->queued_bufs.next, struct msi2500_frame_buf, list);list_del (&buf->list); -vb2_buffer_done (&buf->vb.vb2_buf,VB2_BUF_STATE_ERROR ); +vb2_buffer_done (&buf->vb.vb2_buf, state); }spin_unlock_irqrestore (&dev->queued_bufs_lock, flags); }
@@ -830,25 +831,40 @@ static int msi2500_start_streaming(strucdev_dbg (dev->dev,"\n" ); -if (!dev->udev) -return -ENODEV ; +if (!dev->udev) { + ret = -ENODEV ; + goto err_cleanup; + } -if (mutex_lock_interruptible (&dev->v4l2_lock)) -return -ERESTARTSYS ; +if (mutex_lock_interruptible (&dev->v4l2_lock)) { + ret = -ERESTARTSYS ; + goto err_cleanup; + }/* wake-up tuner */ v4l2_subdev_call (dev->v4l2_subdev, core, s_power,1 ); ret =msi2500_set_usb_adc (dev); +if (ret) + goto err_unlock_cleanup; ret =msi2500_isoc_init (dev);if (ret) -msi2500_cleanup_queued_bufs (dev); + goto err_unlock_cleanup; ret =msi2500_ctrl_msg (dev,CMD_START_STREAMING ,0 ); +if (ret) + goto err_isoc_cleanup;mutex_unlock (&dev->v4l2_lock); +return 0 ; +err_isoc_cleanup: +msi2500_isoc_cleanup (dev); +err_unlock_cleanup: +mutex_unlock (&dev->v4l2_lock); +err_cleanup: +msi2500_cleanup_queued_bufs (dev,VB2_BUF_STATE_QUEUED );return ret; }
@@ -863,7 +879,7 @@ static void msi2500_stop_streaming(strucif (dev->udev)msi2500_isoc_cleanup (dev); -msi2500_cleanup_queued_bufs (dev); +msi2500_cleanup_queued_bufs (dev,VB2_BUF_STATE_ERROR );/* according to tests, at least 700us delay is required */ msleep (20 );
Greg Kroah-Hartman
maintainerPATCH 164/456 media: pci: dm1105: Free allocated workqueue
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krzysztof Kozlowski <[email protected]>
commit 1a65db225b25bb8c8febf16974c060e0cc242eb9 upstream.
Destroy allocated workqueue in remove() callback to free its resources, thus fixing memory leak.
--- drivers/media/pci/dm1105/dm1105.c | 1 + 1 file changed, 1 insertion(+)
drivers/media/pci/dm1105/dm1105.c +1 -0
@@ -1193,6 +1193,7 @@ static void dm1105_remove(struct pci_devdm1105_hw_exit (dev);free_irq (pdev->irq, dev); +destroy_workqueue (dev->wq);pci_iounmap (pdev, dev->io_mem);pci_release_regions (pdev);pci_disable_device (pdev);
Greg Kroah-Hartman
maintainerPATCH 165/456 media: pwc: Drain fill_buf on start_streaming() failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Valery Borovsky <[email protected]>
commit 906e410dcffbbd99fb4081abab817a830033aa28 upstream.
pwc_isoc_init() submits its isochronous URBs with usb_submit_urb(.., GFP_KERNEL) in a loop. After the first URB is submitted, its completion handler pwc_isoc_handler() can run on another CPU before the loop finishes:
start_streaming()
pwc_isoc_init()
usb_submit_urb(urbs[0], GFP_KERNEL)
pwc_isoc_handler(urbs[0])
pdev->fill_buf =
pwc_get_next_fill_buf(pdev)
usb_submit_urb(urbs[i>0], ..) -> fails
pwc_isoc_cleanup(pdev) /* kills URBs */
return ret;
pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED)pwc_get_next_fill_buf() detaches a buffer from pdev->queued_bufs and stores it in pdev->fill_buf. The error path in start_streaming() only drains pdev->queued_bufs, so the buffer parked in pdev->fill_buf is leaked. vb2_start_streaming() then triggers WARN_ON(owned_by_drv_count).
stop_streaming() already handles this since commit 80b0963e1698 ("[media] pwc: fix WARN_ON"), which added the fill_buf drain in the teardown path but not in the start_streaming() error path. Mirror that handling on failure so start_streaming() returns with no buffer owned by the driver.
Issue identified by automated review of the INV-003 series at
https://sashiko.dev/
--- drivers/media/usb/pwc/pwc-if.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/media/usb/pwc/pwc-if.c +5 -0
@@ -727,6 +727,11 @@ static int start_streaming(struct vb2_qupwc_camera_power (pdev,0 );/* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs (pdev,VB2_BUF_STATE_QUEUED ); +if (pdev->fill_buf) { +vb2_buffer_done (&pdev->fill_buf->vb.vb2_buf, +VB2_BUF_STATE_QUEUED ); + pdev->fill_buf =NULL ; + } }mutex_unlock (&pdev->v4l2_lock);
Greg Kroah-Hartman
maintainerPATCH 166/456 media: pwc: Return queued buffers on start_streaming() failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Valery Borovsky <[email protected]>
commit 975b2ee20e569d47821e4f6c9761b4664d48a6a4 upstream.
The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.
pwc's start_streaming() had two early returns that hit this trap:-ENODEV when the USB device was already disconnected, and -ERESTARTSYS when mutex_lock_interruptible() was interrupted by a signal. Call the existing pwc_cleanup_queued_bufs() helper with VB2_BUF_STATE_QUEUED before returning (matching the state already used by the pwc_isoc_init() error path in the same function).
This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").
--- drivers/media/usb/pwc/pwc-if.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/media/usb/pwc/pwc-if.c +6 -2
@@ -711,11 +711,15 @@ static int start_streaming(struct vb2_qustruct pwc_device *pdev =vb2_get_drv_priv (vq);int r; -if (!pdev->udev) +if (!pdev->udev) { +pwc_cleanup_queued_bufs (pdev,VB2_BUF_STATE_QUEUED );return -ENODEV ; + } -if (mutex_lock_interruptible (&pdev->v4l2_lock)) +if (mutex_lock_interruptible (&pdev->v4l2_lock)) { +pwc_cleanup_queued_bufs (pdev,VB2_BUF_STATE_QUEUED );return -ERESTARTSYS ; + }/* Turn on camera and set LEDS on */ pwc_camera_power (pdev,1 );pwc_set_leds (pdev, leds[0 ], leds[1 ]);
Greg Kroah-Hartman
maintainerPATCH 167/456 media: radio-si476x: Unregister v4l2_device on probe failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Myeonghun Pak <[email protected]>
commit 436a693af04ffb889aaf87cb69ec1f2b21d3569c upstream.
si476x_radio_probe() registers radio->v4l2dev before allocating the V4L2 controls and before registering the video device. If any of those later steps fails, probe returns through the exit label after freeing only the control handler.
A failed probe does not call si476x_radio_remove(), so the v4l2_device_unregister() there is not reached. This leaves the parent device reference taken by v4l2_device_register() behind on the error path.
Unregister the V4L2 device in the probe error path after freeing the controls.
--- drivers/media/radio/radio-si476x.c | 1 + 1 file changed, 1 insertion(+)
drivers/media/radio/radio-si476x.c +1 -0
@@ -1504,6 +1504,7 @@ static int si476x_radio_probe(struct plareturn 0 ; exit:v4l2_ctrl_handler_free (radio->videodev.ctrl_handler); +v4l2_device_unregister (&radio->v4l2dev);return rval; }
Greg Kroah-Hartman
maintainerPATCH 168/456 media: rtl2832: fix use-after-free in rtl2832_remove()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
commit 680daf40a82d483949f87f0d8f98639dc47e610c upstream.
cancel_delayed_work_sync() is called before i2c_mux_del_adapters() in rtl2832_remove(). While the cancel waits for any running instance of i2c_gate_work to finish, it does not prevent the timer from being rescheduled by a concurrent thread.
During probe, the r820t_attach() call attempts I2C transfers through the mux adapter. These transfers go through i2c_mux_master_xfer(), which calls rtl2832_deselect() after the transfer completes, rescheduling i2c_gate_work via schedule_delayed_work(). If this transfer is still in flight when rtl2832_remove() runs, rtl2832_deselect() can reschedule i2c_gate_work after it has been cancelled, causing a use-after-free when kfree(dev) is called.
Fix this by calling i2c_mux_del_adapters() before cancel_delayed_work_sync(). Once the mux adapter is unregistered, no new I2C transfers can go through it, so rtl2832_deselect() can no longer reschedule i2c_gate_work. The subsequent cancel_delayed_work_sync() is then guaranteed to be final.
--- drivers/media/dvb-frontends/rtl2832.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/media/dvb-frontends/rtl2832.c +2 -2
@@ -1116,10 +1116,10 @@ static int rtl2832_remove(struct i2c_clidev_dbg (&client->dev,"\n" ); -cancel_delayed_work_sync (&dev->i2c_gate_work); -i2c_mux_del_adapters (dev->muxc); +cancel_delayed_work_sync (&dev->i2c_gate_work); +regmap_exit (dev->regmap);kfree (dev);
Greg Kroah-Hartman
maintainerPATCH 169/456 media: rtl2832_sdr: Return queued buffers on start_streaming() failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Valery Borovsky <[email protected]>
commit 33ca0aab6f4bd90921fc1395478f38f72c4d19af upstream.
The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.
rtl2832_sdr_start_streaming() had multiple error paths that hit this
trap: two direct early returns (-ENODEV, -ERESTARTSYS), plus six goto err paths covering subdev s_power, tuner setup, ADC setup, stream-buffer allocation, urb allocation, and urb submission failures. None of them returned the queued buffers.
The original function had no distinct success exit and fell straight through into the err label, which previously only did mutex_unlock and "return ret". Adding queued-buffer cleanup at err must therefore be paired with an explicit success return; otherwise every successful start would also drain the buffer queue and kill streaming. Add that success return, then add rtl2832_sdr_cleanup_queued_bufs() at the err label and before each early return.
The cleanup helper takes a vb2_buffer_state argument so that the start_streaming error paths can pass VB2_BUF_STATE_QUEUED (as expected by userspace on start_streaming failure) while stop_streaming keeps its existing VB2_BUF_STATE_ERROR semantics.
This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").
The err label still does not roll back power_ctrl(), frontend_ctrl(), the POWER_ON flag, or stream/URB allocations that may have happened before the failing step. Those are pre-existing leaks of a different class and are not addressed here.
--- drivers/media/dvb-frontends/rtl2832_sdr.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-)
drivers/media/dvb-frontends/rtl2832_sdr.c +14 -5
@@ -396,7 +396,8 @@ static int rtl2832_sdr_alloc_urbs(struct }/* Must be called with vb_queue_lock hold */ -static void rtl2832_sdr_cleanup_queued_bufs (struct rtl2832_sdr_dev *dev) +static void rtl2832_sdr_cleanup_queued_bufs (struct rtl2832_sdr_dev *dev, +enum vb2_buffer_state state) {struct platform_device *pdev = dev->pdev;unsigned long flags;
@@ -410,7 +411,7 @@ static void rtl2832_sdr_cleanup_queued_b buf =list_entry (dev->queued_bufs.next, struct rtl2832_sdr_frame_buf, list);list_del (&buf->list); -vb2_buffer_done (&buf->vb.vb2_buf,VB2_BUF_STATE_ERROR ); +vb2_buffer_done (&buf->vb.vb2_buf, state); }spin_unlock_irqrestore (&dev->queued_bufs_lock, flags); }
@@ -851,11 +852,15 @@ static int rtl2832_sdr_start_streaming(sdev_dbg (&pdev->dev,"\n" ); -if (!dev->udev) +if (!dev->udev) { +rtl2832_sdr_cleanup_queued_bufs (dev,VB2_BUF_STATE_QUEUED );return -ENODEV ; + } -if (mutex_lock_interruptible (&dev->v4l2_lock)) +if (mutex_lock_interruptible (&dev->v4l2_lock)) { +rtl2832_sdr_cleanup_queued_bufs (dev,VB2_BUF_STATE_QUEUED );return -ERESTARTSYS ; + }if (d->props->power_ctrl) d->props->power_ctrl (d,1 );
@@ -896,7 +901,11 @@ static int rtl2832_sdr_start_streaming(sif (ret) goto err; +mutex_unlock (&dev->v4l2_lock); +return 0 ; + err: +rtl2832_sdr_cleanup_queued_bufs (dev,VB2_BUF_STATE_QUEUED );mutex_unlock (&dev->v4l2_lock);return ret;
@@ -916,7 +925,7 @@ static void rtl2832_sdr_stop_streaming(srtl2832_sdr_kill_urbs (dev);rtl2832_sdr_free_urbs (dev);rtl2832_sdr_free_stream_bufs (dev); -rtl2832_sdr_cleanup_queued_bufs (dev); +rtl2832_sdr_cleanup_queued_bufs (dev,VB2_BUF_STATE_ERROR );rtl2832_sdr_unset_adc (dev);/* sleep tuner */
Greg Kroah-Hartman
maintainerPATCH 170/456 media: saa7134: Fix a possible memory leak in saa7134_video_init1
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ma Ke <[email protected]>
commit f86ed548386e3050e5f8f25b450d09dc009d9a88 upstream.
In saa7134_video_init1(), the return value of the first saa7134_pgtable_alloc() is not checked. If it fails, the function continues as if successful, leaving the driver with an invalid page table. Additionally, if vb2_queue_init() for the VBI queue fails after the video queue page table has been allocated, the allocated memory is not freed before returning. The second saa7134_pgtable_alloc() also lacks a return value check. Errors occur during device probing before the device is fully registered, the normal cleanup path in saa7134_finidev() is not executed, leading to memory leaks and potential use of uninitialized DMA resources.
Check the return value of both saa7134_pgtable_alloc() calls and propagate errors. On failure of any later step, free allocated page tables to avoid memory leaks. Ensure control handlers are also released on error to prevent further resource leakage.
Found by code review.
--- drivers/media/pci/saa7134/saa7134-video.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
drivers/media/pci/saa7134/saa7134-video.c +20 -5
@@ -2126,8 +2126,10 @@ int saa7134_video_init1(struct saa7134_d q->dev = &dev->pci->dev; ret =vb2_queue_init (q);if (ret) -return ret; -saa7134_pgtable_alloc (dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + ret =saa7134_pgtable_alloc (dev->pci, &dev->video_q.pt); +if (ret) + goto err_free_ctrl; q = &dev->vbi_vbq; q->type =V4L2_BUF_TYPE_VBI_CAPTURE ;
@@ -2144,11 +2146,24 @@ int saa7134_video_init1(struct saa7134_d q->lock = &dev->lock; q->dev = &dev->pci->dev; ret =vb2_queue_init (q); -if (ret) -return ret; -saa7134_pgtable_alloc (dev->pci, &dev->vbi_q.pt); +if (ret) { +saa7134_pgtable_free (dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + } + + ret =saa7134_pgtable_alloc (dev->pci, &dev->vbi_q.pt); +if (ret) { +saa7134_pgtable_free (dev->pci, &dev->video_q.pt); + goto err_free_ctrl; + }return 0 ; + +err_free_ctrl: +v4l2_ctrl_handler_free (&dev->ctrl_handler); +if (card_has_radio (dev)) +v4l2_ctrl_handler_free (&dev->radio_ctrl_handler); +return ret; }void saa7134_video_fini (struct saa7134_dev *dev)
Greg Kroah-Hartman
maintainerPATCH 171/456 media: sun4i-csi: Return queued buffers on start_streaming() failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Valery Borovsky <[email protected]>
commit bbba3e260a62810a717b4442a3bb96d0ec0f6309 upstream.
The vb2 framework hands buffers to the driver via buf_queue() before calling start_streaming(). If start_streaming() returns an error without first returning those buffers via vb2_buffer_done(), vb2_start_streaming() fires WARN_ON(owned_by_drv_count) and the queued buffers leak.
sun4i_csi_start_streaming() returned -EINVAL when no matching CSI format could be found, before any setup (scratch buffer allocation, pipeline start) had been performed. The remaining error paths already converge on the err_clear_dma_queue label, which calls return_all_buffers(..., VB2_BUF_STATE_QUEUED) under csi->qlock. Jump to that label directly: the intermediate err_disable_device / err_disable_pipeline / err_free_scratch_buffer labels are skipped, which is correct because nothing they would undo has happened yet.
This mirrors the uvcvideo fix in commit 4cf3b6fd54eb ("media: uvcvideo:
Return queued buffers on start_streaming() failure").
--- drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c +4 -2
@@ -234,8 +234,10 @@ static int sun4i_csi_start_streaming(strint ret; csi_fmt =sun4i_csi_find_format (&csi->fmt.pixelformat,NULL ); -if (!csi_fmt) -return -EINVAL ; +if (!csi_fmt) { + ret = -EINVAL ; + goto err_clear_dma_queue; + }dev_dbg (csi->dev,"Starting capture\n" );
Greg Kroah-Hartman
maintainerPATCH 172/456 media: tegra-video: vi: fix invalid u32 return value in format lookup
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hungyu Lin <[email protected]>
commit d5b50055338e131a1a99f923ebb0361974a00f36 upstream.
tegra_get_format_fourcc_by_idx() returns a u32 but uses -EINVAL to signal an out-of-bounds index. This results in a large unsigned value being returned, which may be interpreted as a valid fourcc.
Returning 0 is not a valid fourcc either. This condition should never happen, so use WARN_ON_ONCE() to catch unexpected out-of-bounds access and return a valid fallback format instead.
--- drivers/staging/media/tegra-video/vi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/staging/media/tegra-video/vi.c +2 -2
@@ -76,8 +76,8 @@ static int tegra_get_format_idx_by_code(static u32 tegra_get_format_fourcc_by_idx (struct tegra_vi *vi,unsigned int index) { -if (index >= vi->soc->nformats) -return -EINVAL ; +if (WARN_ON_ONCE (index >= vi->soc->nformats)) +return vi->soc->video_formats[0 ].fourcc;return vi->soc->video_formats[index].fourcc; }
Greg Kroah-Hartman
maintainerPATCH 173/456 media: v4l2-ctrls-request: add NULL check in v4l2_ctrl_request_complete()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Shtylyov <[email protected]>
commit caced3578bf9f104a4aaad8f46c4c719e705d9a6 upstream.
If CONFIG_MEDIA_CONTROLLER is undefined, media_request_object_find() will always return NULL, so its 2nd call in v4l2_ctrl_request_complete() would fail as well as the 1st one and thus cause hdl to have a wrong value (at the top of memory) and list_for_each_entry() to iterate over the garbage data located there. Add NULL check for the 2nd call and place the error cleanup at the end of v4l2_ctrl_request_complete()...
Found by Linux Verification Center (linuxtesting.org) with the Svace static analysis tool.
--- drivers/media/v4l2-core/v4l2-ctrls-request.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/media/v4l2-core/v4l2-ctrls-request.c +9 -5
@@ -348,13 +348,12 @@ void v4l2_ctrl_request_complete(struct m ret =v4l2_ctrl_handler_init (hdl, (main_hdl->nr_of_buckets -1 ) *8 );if (!ret) ret =v4l2_ctrl_request_bind (req, hdl, main_hdl); -if (ret) { -v4l2_ctrl_handler_free (hdl); -kfree (hdl); -return ; - } +if (ret) + goto error; hdl->request_is_queued = true; obj =media_request_object_find (req, &req_ops, main_hdl); +if (!obj) + goto error; } hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
@@ -389,6 +388,11 @@ void v4l2_ctrl_request_complete(struct mmutex_unlock (main_hdl->lock);media_request_object_complete (obj);media_request_object_put (obj); +return ; + +error: +v4l2_ctrl_handler_free (hdl); +kfree (hdl); }EXPORT_SYMBOL (v4l2_ctrl_request_complete);
Greg Kroah-Hartman
maintainerPATCH 174/456 media: vb2: use ssize_t for vb2_read/vb2_write
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zile Xiong <[email protected]>
commit a562d6dc86bdfdd299e1b4734977a8d63e803583 upstream.
vb2_read() and vb2_write() return size_t, but propagate negative errno values from __vb2_perform_fileio().
This relies on implicit signed/unsigned conversions in callers (e.g. vb2_fop_read()) to recover error codes:
__vb2_perform_fileio() -> -EINVAL
vb2_read() -> (size_t)-EINVAL
vb2_fop_read() -> -EINVALThis relies on implicit conversions that are not obvious.
These helpers are exported (EXPORT_SYMBOL_GPL) and part of the vb2 API, so changing their return type may affect existing users.
However, they conceptually follow read/write semantics, where ssize_t is typically used to return either a byte count or a negative error code.
Switch vb2_read() and vb2_write() to ssize_t, and update __vb2_perform_fileio() accordingly.
--- drivers/media/common/videobuf2/videobuf2-core.c | 12 ++++++------ include/media/videobuf2-core.h | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-)
drivers/media/common/videobuf2/videobuf2-core.c +6 -6
@@ -2719,8 +2719,8 @@ static int __vb2_cleanup_fileio(struct v* @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) * @read: access mode selector (1 means read, 0 means write) */ -static size_t __vb2_perform_fileio (struct vb2_queue *q,char __user *data,size_t count, -loff_t *ppos,int nonblock,int read) +static ssize_t __vb2_perform_fileio (struct vb2_queue *q,char __user *data,size_t count, +loff_t *ppos,int nonblock,int read) {struct vb2_fileio_data *fileio;struct vb2_fileio_buf *buf;
@@ -2880,15 +2880,15 @@ static size_t __vb2_perform_fileio(strucreturn ret; } -size_t vb2_read (struct vb2_queue *q,char __user *data,size_t count, -loff_t *ppos,int nonblocking) +ssize_t vb2_read (struct vb2_queue *q,char __user *data,size_t count, +loff_t *ppos,int nonblocking) {return __vb2_perform_fileio (q, data, count, ppos, nonblocking,1 ); }EXPORT_SYMBOL_GPL (vb2_read); -size_t vb2_write (struct vb2_queue *q,const char __user *data,size_t count, -loff_t *ppos,int nonblocking) +ssize_t vb2_write (struct vb2_queue *q,const char __user *data,size_t count, +loff_t *ppos,int nonblocking) {return __vb2_perform_fileio (q, (char __user *) data, count, ppos, nonblocking,0 );
include/media/videobuf2-core.h +4 -4
@@ -1044,8 +1044,8 @@ __poll_t vb2_core_poll(struct vb2_queue* @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_read (struct vb2_queue *q,char __user *data,size_t count, -loff_t *ppos,int nonblock); +ssize_t vb2_read (struct vb2_queue *q,char __user *data,size_t count, +loff_t *ppos,int nonblock);/** * vb2_write() - implements write() syscall logic. * @q: pointer to &struct vb2_queue with videobuf2 queue.
@@ -1054,8 +1054,8 @@ size_t vb2_read(struct vb2_queue *q, cha* @ppos: file handle position tracking pointer * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) */ -size_t vb2_write (struct vb2_queue *q,const char __user *data,size_t count, -loff_t *ppos,int nonblock); +ssize_t vb2_write (struct vb2_queue *q,const char __user *data,size_t count, +loff_t *ppos,int nonblock);/** * typedef vb2_thread_fnc - callback function for use with vb2_thread.
Greg Kroah-Hartman
maintainerPATCH 175/456 media: vidtv: fix reference leak on failed device registration
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 9aa21e1549db8882ff77b691e7714153df21dff0 upstream.
When platform_device_register() fails in vidtv_bridge_init(), the embedded struct device in vidtv_bridge_dev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device:
vidtv_bridge_init()
-> platform_device_register(&vidtv_bridge_dev)
-> device_initialize(&vidtv_bridge_dev.dev)
-> setup_pdev_dma_masks(&vidtv_bridge_dev)
-> platform_device_add(&vidtv_bridge_dev)This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error.
The issue was identified by a static analysis tool I developed and confirmed by manual review.
--- drivers/media/test-drivers/vidtv/vidtv_bridge.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/media/test-drivers/vidtv/vidtv_bridge.c +3 -1
@@ -596,8 +596,10 @@ static int __init vidtv_bridge_init(voidint ret; ret =platform_device_register (&vidtv_bridge_dev); -if (ret) +if (ret) { +platform_device_put (&vidtv_bridge_dev);return ret; + } ret =platform_driver_register (&vidtv_bridge_driver);if (ret)
Greg Kroah-Hartman
maintainerPATCH 176/456 media: vimc: fix reference leak on failed device registration
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 33e2b833c66b890a0d71c4fa82d4c97143f7f75f upstream.
When platform_device_register() fails in vimc_init(), the embedded struct device in vimc_pdev has already been initialized by device_initialize(), but the failure path returns the error without dropping the device reference for the current platform device:
vimc_init()
-> platform_device_register(&vimc_pdev)
-> device_initialize(&vimc_pdev.dev)
-> setup_pdev_dma_masks(&vimc_pdev)
-> platform_device_add(&vimc_pdev)This leads to a reference leak when platform_device_register() fails. Fix this by calling platform_device_put() before returning the error.
The issue was identified by a static analysis tool I developed and confirmed by manual review.
--- drivers/media/test-drivers/vimc/vimc-core.c | 1 + 1 file changed, 1 insertion(+)
drivers/media/test-drivers/vimc/vimc-core.c +1 -0
@@ -350,6 +350,7 @@ static int __init vimc_init(void)if (ret) {dev_err (&vimc_pdev.dev,"platform device registration failed (err=%d)\n" , ret); +platform_device_put (&vimc_pdev);return ret; }
Greg Kroah-Hartman
maintainerPATCH 177/456 media: vivid: check for vb2_is_busy() when toggling caps
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hans Verkuil <[email protected]>
commit c2d1a2130c93f6d758af58590b86b2254c7a1dec upstream.
The vivid_update_format_cap/out() functions must only be called if the capture/output queue are not busy. But for the controls that select the CROP/COMPOSE/SCALE capability that is not checked.
Only when streaming starts will they be set to 'grabbed' and it is impossible to change the control, but between REQBUFS and STREAMON you are still allowed to set these controls. Since vivid_update_format_cap/out will change the format, this can cause unexpected results.
Besides adding these checks, also add a WARN_ON in vivid_update_format_cap/out() if the queue is busy.
I'm 90% certain that this is the cause of this syzbot bug:
https://syzkaller.appspot.com/bug?extid=dac8f5eaa46837e97b89
But since we never have reproducers, it is hard to be certain. In any case, these checks are needed regardless.
--- drivers/media/test-drivers/vivid/vivid-ctrls.c | 12 ++++++++++++ drivers/media/test-drivers/vivid/vivid-vid-cap.c | 6 ++++++ drivers/media/test-drivers/vivid/vivid-vid-out.c | 6 ++++++ 3 files changed, 24 insertions(+)
drivers/media/test-drivers/vivid/vivid-ctrls.c +12 -0
@@ -487,14 +487,20 @@ static int vivid_vid_cap_s_ctrl(struct vvivid_update_format_cap (dev, true);break ;case VIVID_CID_HAS_CROP_CAP : +if (vb2_is_busy (&dev->vb_vid_cap_q)) +return -EBUSY ; dev->has_crop_cap = ctrl->val;vivid_update_format_cap (dev, true);break ;case VIVID_CID_HAS_COMPOSE_CAP : +if (vb2_is_busy (&dev->vb_vid_cap_q)) +return -EBUSY ; dev->has_compose_cap = ctrl->val;vivid_update_format_cap (dev, true);break ;case VIVID_CID_HAS_SCALER_CAP : +if (vb2_is_busy (&dev->vb_vid_cap_q)) +return -EBUSY ; dev->has_scaler_cap = ctrl->val;vivid_update_format_cap (dev, true);break ;
@@ -970,14 +976,20 @@ static int vivid_vid_out_s_ctrl(struct vswitch (ctrl->id) {case VIVID_CID_HAS_CROP_OUT : +if (vb2_is_busy (&dev->vb_vid_out_q)) +return -EBUSY ; dev->has_crop_out = ctrl->val;vivid_update_format_out (dev);break ;case VIVID_CID_HAS_COMPOSE_OUT : +if (vb2_is_busy (&dev->vb_vid_out_q)) +return -EBUSY ; dev->has_compose_out = ctrl->val;vivid_update_format_out (dev);break ;case VIVID_CID_HAS_SCALER_OUT : +if (vb2_is_busy (&dev->vb_vid_out_q)) +return -EBUSY ; dev->has_scaler_out = ctrl->val;vivid_update_format_out (dev);break ;
drivers/media/test-drivers/vivid/vivid-vid-cap.c +6 -0
@@ -385,6 +385,12 @@ void vivid_update_format_cap(struct viviunsigned size;u64 pixelclock; +/* +* This resets the format, so must never be called while vb2_is_busy(). +*/ +if (WARN_ON (vb2_is_busy (&dev->vb_vid_cap_q))) +return ; +switch (dev->input_type[dev->input]) {case WEBCAM :default :
drivers/media/test-drivers/vivid/vivid-vid-out.c +6 -0
@@ -222,6 +222,12 @@ void vivid_update_format_out(struct viviunsigned size, p;u64 pixelclock; +/* +* This resets the format, so must never be called while vb2_is_busy(). +*/ +if (WARN_ON (vb2_is_busy (&dev->vb_vid_out_q))) +return ; +switch (dev->output_type[dev->output]) {case SVID :default :
Greg Kroah-Hartman
maintainerPATCH 178/456 wifi: ath6kl: fix OOB access from firmware ADDBA window size
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tristan Madani <[email protected]>
commit 44126b6994eeb28f2103b638e698f40a1244f327 upstream.
aggr_recv_addba_req_evt() logs a debug message when the firmware-supplied win_sz is outside [AGGR_WIN_SZ_MIN, AGGR_WIN_SZ_MAX] but does not return. The out-of-range win_sz is then used in TID_WINDOW_SZ() to compute a kzalloc size and stored in rxtid->hold_q_sz, leading to zero-size or overflowed allocations and subsequent out-of-bounds access.
Clean up any previously active aggregation session for the TID first, then return early when win_sz is out of the valid range, instead of proceeding with a broken allocation size.
--- drivers/net/wireless/ath/ath6kl/txrx.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
drivers/net/wireless/ath/ath6kl/txrx.c +6 -4
@@ -1722,13 +1722,15 @@ void aggr_recv_addba_req_evt(struct ath6 rxtid = &aggr_conn->rx_tid[tid]; -if (win_sz <AGGR_WIN_SZ_MIN || win_sz >AGGR_WIN_SZ_MAX ) -ath6kl_dbg (ATH6KL_DBG_WLAN_RX ,"%s: win_sz %d, tid %d\n" , - __func__, win_sz, tid); -if (rxtid->aggr)aggr_delete_tid_state (aggr_conn, tid); +if (win_sz <AGGR_WIN_SZ_MIN || win_sz >AGGR_WIN_SZ_MAX ) { +ath6kl_dbg (ATH6KL_DBG_WLAN_RX ,"%s: win_sz %d, tid %d\n" , + __func__, win_sz, tid); +return ; + } + rxtid->seq_next = seq_no; hold_q_size =TID_WINDOW_SZ (win_sz) *sizeof (struct skb_hold_q ); rxtid->hold_q =kzalloc (hold_q_size,GFP_KERNEL );
Greg Kroah-Hartman
maintainerPATCH 179/456 wifi: mwifiex: fix NULL dereference when the AP has HT-cap but no HT-oper
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit c3d68e294cbb6a4090bb219d3dcaca85a011809b upstream.
mwifiex_tdls_add_ht_oper() gates its follow-the-AP-bandwidth path on bss_desc->bcn_ht_cap being present, but then dereferences a different pointer, bss_desc->bcn_ht_oper:
if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && bss_desc->bcn_ht_cap && ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param))
bcn_ht_cap and bcn_ht_oper are populated independently while parsing the associated AP's beacon in mwifiex_update_bss_desc_with_ie(): an AP that advertises an HT Capabilities element but no HT Operation element leaves bcn_ht_cap non-NULL and bcn_ht_oper NULL. Setting up a TDLS link to a peer while associated to such an AP then dereferences the NULL bcn_ht_oper and crashes the kernel. Every other bcn_ht_oper user in the driver NULL-checks it first.
Guard on the pointer that is actually dereferenced.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- drivers/net/wireless/marvell/mwifiex/tdls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/marvell/mwifiex/tdls.c +1 -1
@@ -215,7 +215,7 @@ mwifiex_tdls_add_ht_oper(struct mwifiex_/* follow AP's channel bandwidth */ if (ISSUPP_CHANWIDTH40 (priv->adapter->hw_dot_11n_dev_cap) && - bss_desc->bcn_ht_cap && + bss_desc->bcn_ht_oper &&ISALLOWED_CHANWIDTH40 (bss_desc->bcn_ht_oper->ht_param)) ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param;
Greg Kroah-Hartman
maintainerPATCH 180/456 wifi: wilc1000: validate assoc response length before subtracting header
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huihui Huang <[email protected]>
commit 4c4c97b60a5e978121d9ee8cb0ab3916e5d6a8de upstream.
wilc_parse_assoc_resp_info() computes the trailing IE length as
ies_len = buffer_len - sizeof(*res);
without first checking that buffer_len is at least sizeof(struct wilc_assoc_resp) (6 bytes). buffer_len is the length reported for a received association response (host_int_parse_assoc_resp_info() passes hif_drv->assoc_resp / assoc_resp_info_len straight in) and must be validated before the driver accesses the fixed header.
For a frame shorter than the 6-byte fixed header, the subtraction wraps. For a four-byte response the result is truncated to a u16 ies_len of 65534, so kmemdup() then attempts to copy 65534 bytes starting at buffer + sizeof(*res), beyond the valid association-response data (CWE-125). A response shorter than four bytes can also cause an out-of-bounds read of res->status_code at offsets 2 and 3.
Reject frames too short to hold the fixed header before touching the header or computing ies_len. Also set the connection status to a failure on this path: the caller falls through to a "conn_info->status == WLAN_STATUS_SUCCESS" check after the parser returns, so leaving the status untouched could let a malformed short response be treated as a successful association.
--- drivers/net/wireless/microchip/wilc1000/hif.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/net/wireless/microchip/wilc1000/hif.c +5 -0
@@ -582,6 +582,11 @@ static s32 wilc_parse_assoc_resp_info(u8u16 ies_len;struct wilc_assoc_resp *res = (struct wilc_assoc_resp *)buffer; +if (buffer_len <sizeof (*res)) { + ret_conn_info->status =WLAN_STATUS_UNSPECIFIED_FAILURE ; +return -EINVAL ; + } + ret_conn_info->status =le16_to_cpu (res->status_code);if (ret_conn_info->status ==WLAN_STATUS_SUCCESS ) { ies = &buffer[sizeof (*res)];
Greg Kroah-Hartman
maintainerPATCH 181/456 wifi: brcmfmac: make release_scratchbuffers idempotent
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 538c51e9d124cf656f2dd0c0394a8545efc7102d upstream.
brcmf_pcie_release_scratchbuffers() frees the shared.scratch and shared.ringupd DMA buffers with dma_free_coherent() but does not clear the pointers afterwards, unlike the sibling release_ringbuffers() which NULLs commonrings/flowrings/idxbuf on release.
Both the bus_reset .reset callback (brcmf_pcie_reset) and brcmf_pcie_remove() call release_scratchbuffers. When reset teardown has run before removal, remove's own teardown would call dma_free_coherent() a second time on the already-freed DMA allocation.
NULL the pointers after free, matching release_ringbuffers(), so a later release observes that the allocation has already been released. This patch makes repeated sequential release safe; the reset-work lifetime is handled separately by the following patch.
This issue was found by an in-house static analysis tool.
--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +6 -2
@@ -1255,16 +1255,20 @@ fail:static void brcmf_pcie_release_scratchbuffers (struct brcmf_pciedev_info *devinfo) { -if (devinfo->shared.scratch) +if (devinfo->shared.scratch) {dma_free_coherent (&devinfo->pdev->dev,BRCMF_DMA_D2H_SCRATCH_BUF_LEN , devinfo->shared.scratch, devinfo->shared.scratch_dmahandle); -if (devinfo->shared.ringupd) + devinfo->shared.scratch =NULL ; + } +if (devinfo->shared.ringupd) {dma_free_coherent (&devinfo->pdev->dev,BRCMF_DMA_D2H_RINGUPD_BUF_LEN , devinfo->shared.ringupd, devinfo->shared.ringupd_dmahandle); + devinfo->shared.ringupd =NULL ; + } }static int brcmf_pcie_init_scratchbuffers (struct brcmf_pciedev_info *devinfo)
Greg Kroah-Hartman
maintainerPATCH 182/456 staging: rtl8723bs: fix OOB reads in rtw_get_wps_ie()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Moksh Panicker <[email protected]>
commit 0e95ff792ae0aa6fbad9455943e9e1e4062670e9 upstream.
rtw_get_wps_ie() iterates over IE data from network frames without validating that the IE header and payload fit within the remaining buffer before reading them. Specifically:
- in_ie[cnt + 1] is read without checking cnt + 1 < in_len
- memcmp(&in_ie[cnt + 2], ...) accesses cnt + 2 without bounds check
- in_ie[cnt + 1] is used as length without verifying payload fits
Add bounds checks at the top of the loop body to break early if fewer than 2 bytes remain for the IE header, or if the declared payload extends past the end of the buffer. Also require at least 4 bytes of payload before comparing the WPS OUI.
--- drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/staging/rtl8723bs/core/rtw_ieee80211.c +8 -1
@@ -694,7 +694,14 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_lewhile (cnt < in_len) { eid = in_ie[cnt]; -if ((eid ==WLAN_EID_VENDOR_SPECIFIC ) && (!memcmp (&in_ie[cnt +2 ], wps_oui,4 ))) { +if (cnt +2 > in_len) +break ; + +if (in_ie[cnt +1 ] +2 > in_len - cnt) +break ; + +if ((eid ==WLAN_EID_VENDOR_SPECIFIC ) && (in_ie[cnt +1 ] >=4 ) && + (!memcmp (&in_ie[cnt +2 ], wps_oui,4 ))) { wpsie_ptr = &in_ie[cnt];if (wps_ie)
Greg Kroah-Hartman
maintainerPATCH 183/456 staging: rtl8723bs: fix inverted HT40 secondary channel offset
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: MinJea Kim <[email protected]>
commit 30d49cba27f8905bc288cef5846963f0004f644c upstream.
rtw_get_chan_type() maps the driver's channel offset to nl80211 channel types the wrong way around.
In this driver HAL_PRIME_CHNL_OFFSET_LOWER means the primary channel is the lower 20 MHz half of the 40 MHz pair, i.e. the secondary channel is above the primary one: rtw_get_center_ch() computes the center channel as "channel + 2" for OFFSET_LOWER, and bwmode_update_check() sets OFFSET_LOWER when the AP's HT operation IE announces SCA (secondary channel above). In nl80211 terms that is NL80211_CHAN_HT40PLUS, not HT40MINUS.
Because of the inversion, cfg80211_rtw_get_channel() reports an HT40+ association as HT40-. For an HT40+ AP on a low channel (e.g. channel 3) the resulting chandef spans below the 2.4 GHz band edge and is invalid, so the regulatory core tears the connection down 60 seconds (REG_ENFORCE_GRACE_MS) after the AP's country IE triggers a regdomain
change: reg_check_chans_work() considers the reported chandef unusable and calls cfg80211_leave(). The supplicant then reconnects, the country IE changes the regdomain again, and the cycle repeats, causing a disconnect/reconnect loop every ~65 seconds for as long as the link is up.
Observed on a TECLAST X80 Power tablet (RTL8723BS) associated to an HT40+ AP on channel 3 with a KR country IE; a kprobe trace showed cfg80211_disconnect() being invoked from reg_check_chans_work(). With the mapping fixed, "iw dev wlan0 info" reports the correct "width: 40 MHz, center1: 2432 MHz" and the periodic disconnects stop.
--- drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +1 -1
@@ -1996,7 +1996,7 @@ static u8 rtw_get_chan_type(struct adaptelse return NL80211_CHAN_NO_HT ;case CHANNEL_WIDTH_40 : -if (mlme_ext->cur_ch_offset ==HAL_PRIME_CHNL_OFFSET_UPPER ) +if (mlme_ext->cur_ch_offset ==HAL_PRIME_CHNL_OFFSET_LOWER )return NL80211_CHAN_HT40PLUS ;else return NL80211_CHAN_HT40MINUS ;
Greg Kroah-Hartman
maintainerPATCH 184/456 Bluetooth: RFCOMM: Fix session UAF in set_termios
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chengfeng Ye <[email protected]>
commit c783399efc22d035443f1dfbf2a09bf9562aaa5e upstream.
rfcomm_tty_set_termios() tests dlc->session without rfcomm_mutex and later passes the pointer to rfcomm_send_rpn(). The latter dereferences both session->initiator and session->sock. Meanwhile, krfcommd can unlink the DLC and free the session while holding rfcomm_mutex.
The race can proceed as follows:
TTY ioctl task krfcommd
-------------- --------
load dlc->session
enter rfcomm_send_rpn()
lock rfcomm_mutex
clear dlc->session
free session
unlock rfcomm_mutex read session->initiator
KASAN reported:
BUG: KASAN: slab-use-after-free in rfcomm_send_rpn+0x297/0x2a0 Read of size 4 at addr ffff88810012a850 by task poc/92 Call Trace: rfcomm_send_rpn+0x297/0x2a0 rfcomm_tty_set_termios+0x50d/0x850 tty_set_termios+0x596/0x950 set_termios+0x46a/0x6e0 tty_mode_ioctl+0x152/0xbd0 tty_ioctl+0x915/0x1240 __x64_sys_ioctl+0x134/0x1c0 Allocated by task 92: rfcomm_session_add+0x9e/0x2e0 rfcomm_dlc_open+0x8b1/0xe00 rfcomm_dev_activate+0x85/0x1a0 rfcomm_tty_open+0x90/0x280 Freed by task 68: kfree+0x131/0x3c0 rfcomm_session_del+0x119/0x180 rfcomm_run+0x737/0x4710
Add rfcomm_dlc_send_rpn(), which holds rfcomm_mutex while it verifies that the DLC is still attached and sends the RPN frame. Have the TTY path use the helper and drop its unlocked session check. This keeps the session valid through both the frame construction and socket send.
--- include/net/bluetooth/rfcomm.h | 3 +++ net/bluetooth/rfcomm/core.c | 17 +++++++++++++++++ net/bluetooth/rfcomm/tty.c | 7 +++---- 3 files changed, 23 insertions(+), 4 deletions(-)
include/net/bluetooth/rfcomm.h +3 -0
@@ -229,6 +229,9 @@ int rfcomm_send_rpn(struct rfcomm_sessio u8 bit_rate, u8 data_bits, u8 stop_bits, u8 parity, u8 flow_ctrl_settings, u8 xon_char, u8 xoff_char, u16 param_mask); +int rfcomm_dlc_send_rpn (struct rfcomm_dlc *d,u8 bit_rate,u8 data_bits, +u8 stop_bits,u8 parity,u8 flow_ctrl_settings, +u8 xon_char,u8 xoff_char,u16 param_mask);/* ---- RFCOMM DLCs (channels) ---- */ struct rfcomm_dlc *rfcomm_dlc_alloc (gfp_t prio);
net/bluetooth/rfcomm/core.c +17 -0
@@ -1027,6 +1027,23 @@ int rfcomm_send_rpn(struct rfcomm_sessioreturn rfcomm_send_frame (s, buf, ptr - buf); } +int rfcomm_dlc_send_rpn (struct rfcomm_dlc *d,u8 bit_rate,u8 data_bits, +u8 stop_bits,u8 parity,u8 flow_ctrl_settings, +u8 xon_char,u8 xoff_char,u16 param_mask) +{ +int err = -ENOTCONN ; + +rfcomm_lock (); +if (d->session) + err =rfcomm_send_rpn (d->session,1 , d->dlci, bit_rate, + data_bits, stop_bits, parity, + flow_ctrl_settings, xon_char, xoff_char, + param_mask); +rfcomm_unlock (); + +return err; +} +static int rfcomm_send_rls (struct rfcomm_session *s,int cr,u8 dlci,u8 status) {struct rfcomm_hdr *hdr;
net/bluetooth/rfcomm/tty.c +3 -4
@@ -868,7 +868,7 @@ static void rfcomm_tty_set_termios(strucBT_DBG ("tty %p termios %p" , tty, old); -if (!dev || !dev->dlc || !dev->dlc->session) +if (!dev || !dev->dlc)return ;/* Handle turning off CRTSCTS */
@@ -989,9 +989,8 @@ static void rfcomm_tty_set_termios(struc }if (changes) -rfcomm_send_rpn (dev->dlc->session,1 , dev->dlc->dlci, baud, - data_bits, stop_bits, parity, -RFCOMM_RPN_FLOW_NONE , x_on, x_off, changes); +rfcomm_dlc_send_rpn (dev->dlc, baud, data_bits, stop_bits, parity, +RFCOMM_RPN_FLOW_NONE , x_on, x_off, changes); }static void rfcomm_tty_throttle (struct tty_struct *tty)
Greg Kroah-Hartman
maintainerPATCH 185/456 exec: fix unsigned loop counter wrap in transfer_args_to_stack()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 16cc4f5c1c4b9e45eca7f7deefa5410a292db599 upstream.
The stop value is derived from bprm->p >> PAGE_SHIFT. The index variable is an unsigned long. If bprm->p drops below PAGE_SIZE and stop becomes zero the loop condition index >= stop is always true.
After the index == 0 iteration the decrement wraps to ULONG_MAX and bprm->page[ULONG_MAX] reads sizeof(void *) bytes in front of the array. The pointer has wrapped to -1. That garbage pointer is then passed to kmap_local_page() and PAGE_SIZE bytes are copied from wherever that lands into the stack of the process being created. And the loop doesn't terminate either...
Getting there only requires bprm->p < PAGE_SIZE. On !MMU bprm_set_stack_limit() and bprm_hit_stack_limit() are empty. So the only constraint on how far bprm->p is pushed down is valid_arg_len(), i.e. that each individual string still fits in what is left.
bprm->p starts at PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *) so a single argument or environment string of a little over 31 pages leaves it in the first page:
Oops - load access fault [#1]
CPU: 0 UID: 0 PID: 1 Comm: victim Not tainted 7.2.0-rc4 #1 epc : __memcpy+0xd4/0xf8 ra : transfer_args_to_stack+0xaa/0xae s4 : ffffffffffffffff s2 : 0000000000000000 a1 : ffffffdc98000000 a2 : 0000000000001000 status: 0000000a00001880 badaddr: ffffffdc98000000 cause: 0000000000000005 [<801a5324>] __memcpy+0xd4/0xf8 [<800d5f6a>] load_flat_binary+0x43a/0x65e [<800a2de4>] bprm_execve+0x1d4/0x316 [<800a351a>] do_execveat_common+0x12e/0x138 [<800a3d44>] __riscv_sys_execve+0x38/0x4e Kernel panic - not syncing: Fatal exception in interrupt
This is an arcane bug but we should still fix it.
Count down from MAX_ARG_PAGES so the loop ends when index reaches stop, stop == 0 included. The iterations performed are unchanged for every other value of stop.
Only CONFIG_MMU=n builds are affected, transfer_args_to_stack() is used by binfmt_flat and binfmt_elf_fdpic on nommu only.
The loop predates git history. commit 7e7ec6a93434 ("elf_fdpic_transfer_args_to_stack(): make it generic") only moved it from binfmt_elf_fdpic.c into fs/exec.c and narrowed the copy to the used part of the first page. The condition and the decrement are unchanged from 2.6.12-rc2.
--- fs/exec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/exec.c +1 -1
@@ -876,7 +876,7 @@ int transfer_args_to_stack(struct linux_ stop = bprm->p >>PAGE_SHIFT ; sp = *sp_location; -for (index =MAX_ARG_PAGES -1 ; index >= stop; index--) { +for (index =MAX_ARG_PAGES ; index-- > stop; ) {unsigned int offset = index == stop ? bprm->p & ~PAGE_MASK :0 ;char *src =kmap (bprm->page[index]) + offset; sp -=PAGE_SIZE - offset;
Greg Kroah-Hartman
maintainerPATCH 186/456 binfmt_misc: set have_execfd only once the interpreter is opened
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit bbf5f639918dc011aaf60aab8480218758ee68c5 upstream.
load_misc_binary() raises bprm->have_execfd as soon as it sees the 'O' (or 'C') flag. This happens well before it opens the interpreter. If that open fails the flag stays set on the bprm. binfmt_misc is at the head of the format list so an interpreter open failure that returns -ENOEXEC lets the search fall through to a later format. This means it runs the matched binary directly having never staged an interpreter. So bprm->executable is NULL while have_execfd falsely claims a descriptor is present.
Consequently, begin_new_exec() dereferences the missing executable:
would_dump(bprm, bprm->executable);
and NULL derefs. Had it not, the hand-off later in the same function would have failed anyway. FD_ADD(0, bprm->executable) rejects a NULL file with -ENOMEM. Both sites are past the point of no return so the exec cannot be unwound either way.
This can be reached by unprivileged users as binfmt_misc can be mounted in user namespaces. So a user can register an 'O' entry whose interpreter lives on a FUSE mount, have the FUSE server fail the open with -ENOEXEC and execute a native ELF file that matches the entry.
have_execfd only means anything alongside the executable it describes which is not set until the interpreter has been opened and staged. So lets raise it there, next to execfd_creds, which is already set at that point. An open failure now leaves it clear, so the fallback format derives credentials from the binary and emits no AT_EXECFD, as it would for any native exec. The argv rewrite load_misc_binary() performs before the open is still not undone. This means the binary sees the interpreter path in argv[0] and its own path in argv[1] but that predates this change and only became observable once the exec stopped faulting.
--- fs/binfmt_misc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
fs/binfmt_misc.c +2 -3
@@ -199,9 +199,6 @@ static int load_misc_binary(struct linux goto ret; } -if (fmt->flags &MISC_FMT_OPEN_BINARY ) - bprm->have_execfd =1 ; -/* make argv[1] be the path to the binary */ retval =copy_string_kernel (bprm->interp, bprm);if (retval <0 )
@@ -231,6 +228,8 @@ static int load_misc_binary(struct linux goto ret; bprm->interpreter = interp_file; +if (fmt->flags &MISC_FMT_OPEN_BINARY ) + bprm->have_execfd =1 ;if (fmt->flags &MISC_FMT_CREDENTIALS ) bprm->execfd_creds =1 ;
Greg Kroah-Hartman
maintainerPATCH 187/456 cdrom: fix stack out-of-bounds read in CDROMVOLCTRL
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit b27e195d4db8dea263050bdbeb11881b2999c9c6 upstream.
mmc_ioctl_cdrom_volume() first reads the audio control mode page into a 32-byte stack buffer with cgc->buflen set to 24. If the device reports a block descriptor, the function increases cgc->buflen to include that descriptor and reads the page again.
For CDROMVOLCTRL, the function then builds a MODE SELECT parameter list by moving cgc->buffer forward by offset - 8 bytes. This drops the block descriptor from the outgoing payload and leaves a new 8-byte mode parameter header in front of the audio control page. However, cgc->buflen is left unchanged.
With a standard 8-byte block descriptor, cgc->buffer points at buffer + 8 but cgc->buflen remains 32. cdrom_mode_select() therefore asks the low level packet path to write 32 bytes from that adjusted pointer, reading 8 bytes past the end of the 32-byte stack buffer.
This is not hit by CDROMVOLREAD, and CDROMVOLCTRL only triggers it on drives that return a non-zero block descriptor length, which helps explain why it has gone unnoticed. The overread is also sent to the device as extra MODE SELECT payload, so it may not produce an obvious local failure.
Reduce cgc->buflen by the same amount as the buffer pointer adjustment so the MODE SELECT transfer covers only the intended parameter list.
--- drivers/cdrom/cdrom.c | 1 + 1 file changed, 1 insertion(+)
drivers/cdrom/cdrom.c +1 -0
@@ -3165,6 +3165,7 @@ static noinline int mmc_ioctl_cdrom_volu/* set volume */ cgc->buffer = buffer + offset -8 ; + cgc->buflen -= offset -8 ;memset (cgc->buffer,0 ,8 );return cdrom_mode_select (cdi, cgc); }
Greg Kroah-Hartman
maintainerPATCH 188/456 x86/boot/compressed: Disable jump tables
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nathan Chancellor <[email protected]>
commit 4a9ec5ec9555ad62dc5b81a37ac946025c2ea002 upstream.
After a recent upstream LLVM change to start generating jump and lookup tables in switch statements in more instances [1], linking the compressed x86 boot image when CONFIG_KERNEL_ZSTD is enabled fails with:
ld.lld: error: Unexpected run-time relocations (.rela) detected!
Dumping the relocations in misc.o, which is the only file influenced by CONFIG_KERNEL_ZSTD in the decompressor, shows dynamic relocations to some string constants, which correspond to the string literals in the switch statement in handle_zstd_error():
Relocation section '.rela.data.rel.ro' at offset 0x277b0 contains 31 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 73a 0000000000000008 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e 0000000000000010 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e 0000000000000018 0000006600000001 R_X86_64_64 0000000000000000 .rodata.str1.1 + 78e
...
This optimization is problematic for the decompressor environment, as it is built as -fPIE without any explicit absolute references (as described at the top of misc.c) while not applying any dynamic relocations, hence the linker assertion. To opt out of this optimization, which is of little value in this special early boot code, and to mirror the other x86 startup code in arch/x86/boot/startup, disable jump tables in the decompressor.
--- arch/x86/boot/compressed/Makefile | 1 + 1 file changed, 1 insertion(+)
arch/x86/boot/compressed/Makefile +1 -0
@@ -35,6 +35,7 @@ targets := vmlinux vmlinux.bin vmlinux.b KBUILD_CFLAGS := -m$(BITS) -O2 $(CLANG_FLAGS) KBUILD_CFLAGS += -std=gnu89 KBUILD_CFLAGS += -fno-strict-aliasing -fPIE +KBUILD_CFLAGS += -fno-jump-tables KBUILD_CFLAGS += -Wundef KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING cflags-$(CONFIG_X86_32) := -march=i386
Greg Kroah-Hartman
maintainerPATCH 189/456 comedi: comedi_parport: deal with premature interrupt
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ian Abbott <[email protected]>
commit 17221216ae8ce6a24e8a4e787382e3ebc81b88a8 upstream.
Syzbot reported a general protection fault in comedi_get_is_subdevice_running(), which was called from the interrupt handler parport_interrupt() in the "comedi_parport" driver, but it does not currently have a C reproducer for the problem. It's probably due to a premature interrupt for one of two reasons:
1. The driver sets up the interrupt handler before the comedi subdevices used by the interrupt handler have been allocated, but does not disable the interrupt in the parallel port's CTRL register first.
2. The driver uses a user-supplied I/O port base address which Syzbot would have supplied, but it might not be backed by real parallel port hardware.
Change the initialization order in the driver's comedi "attach" handler (parport_attach()) so that the hardware registers are initialized before the interrupt handler is requested. This should prevent premature interrupts occurring for real hardware.
Also add a test to the interrupt handler to ensure the comedi device is fully attached and return early if it isn't.
--- drivers/comedi/drivers/comedi_parport.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
drivers/comedi/drivers/comedi_parport.c +10 -3
@@ -212,6 +212,13 @@ static irqreturn_t parport_interrupt(intunsigned int ctrl;unsigned short val =0 ; +/* +* Check device is fully attached. Device interrupts should have +* been disabled, but do this in case of bad hardware. +*/ +if (!dev->attached) +return IRQ_NONE ; + ctrl =inb (dev->iobase +PARPORT_CTRL_REG );if (!(ctrl &PARPORT_CTRL_IRQ_ENA ))return IRQ_NONE ;
@@ -232,6 +239,9 @@ static int parport_attach(struct comedi_if (ret)return ret; +outb (0 , dev->iobase +PARPORT_DATA_REG ); +outb (0 , dev->iobase +PARPORT_CTRL_REG ); +if (it->options[1 ]) { ret =request_irq (it->options[1 ], parport_interrupt,0 , dev->board_name, dev);
@@ -287,9 +297,6 @@ static int parport_attach(struct comedi_ s->cancel = parport_intr_cancel; } -outb (0 , dev->iobase +PARPORT_DATA_REG ); -outb (0 , dev->iobase +PARPORT_CTRL_REG ); -return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 190/456 intel_th: fix MSC output device reference leak
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
commit 761b785a0cfbce43761227bc42a7f984f31f8921 upstream.
intel_th_output_open() looks up the output device with bus_find_device_by_devt(), which returns the device with a reference that must be dropped after use.
commit 95fc36a234da ("intel_th: fix device leak on output open()") attempted to drop the reference from intel_th_output_release(). However, a successful open replaces file->f_op with the output driver file operations before returning, so close runs the output driver release callback instead.
For MSC outputs, close runs intel_th_msc_release(), which only removes the per-file iterator and does not drop the device reference taken by intel_th_output_open(). Consequently, every successful MSC output open leaks one device reference.
Drop the device reference from intel_th_msc_release(), which is the release path actually used for MSC output files. Remove the now-unused intel_th_output_release() callback from intel_th_output_fops.
--- drivers/hwtracing/intel_th/core.c | 10 ---------- drivers/hwtracing/intel_th/msu.c | 2 ++ 2 files changed, 2 insertions(+), 10 deletions(-)
drivers/hwtracing/intel_th/core.c +0 -10
@@ -843,18 +843,8 @@ out_put_device:return err; } -static int intel_th_output_release (struct inode *inode,struct file *file) -{ -struct intel_th_device *thdev = file->private_data; - -put_device (&thdev->dev); - -return 0 ; -} -static const struct file_operations intel_th_output_fops = { .open = intel_th_output_open, - .release = intel_th_output_release, .llseek = noop_llseek, };
drivers/hwtracing/intel_th/msu.c +2 -0
@@ -1479,8 +1479,10 @@ static int intel_th_msc_release(struct i {struct msc_iter *iter = file->private_data;struct msc *msc = iter->msc; +struct intel_th_device *thdev = msc->thdev;msc_iter_remove (iter, msc); +put_device (&thdev->dev);return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 191/456 tracing: Fix mmiotrace possible NULL dereferencing of hiter->dev
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
commit 144f29e85702234b23d2a62abf723e6a17eb5427 upstream.
If the mmio_pipe_open() fails to find a PCI device, the hiter->dev will be assigned to NULL. The mmiotrace read() function dereferences the hiter->dev if hiter exists.
Change the test of the read to not only check hiter being NULL, but also the hiter->dev before dereferencing it.
--- kernel/trace/trace_mmiotrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_mmiotrace.c +1 -1
@@ -146,7 +146,7 @@ static ssize_t mmio_read(struct trace_it goto print_out; } -if (!hiter) +if (!hiter || !hiter->dev)return 0 ;mmio_print_pcidev (s, hiter->dev);
Greg Kroah-Hartman
maintainerPATCH 192/456 tracing: Fix resource leak on mmiotrace trace_pipe close
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: deepakraog <[email protected]>
commit c1d87e724ae55e781b7cc7ccafb34d9e668582b2 upstream.
The mmiotrace tracer was added May 12th 2008. At that time, resources created in pipe_open() could not be freed because there was not pipe_close function pointer of the tracer. The pipe_close function pointer was added in December 7th, 2009, but the mmiotrace tracer was not updated.
mmio_pipe_open() allocates a header_iter and takes a pci_dev reference when trace_pipe is opened. mmio_close() frees them, but it was only wired to the tracer's .close callback.
tracing_release_pipe() invokes .pipe_close, not .close, when the trace_pipe file is released. As a result, closing trace_pipe with the mmiotrace tracer active leaked the header_iter allocation and left a stale pci_dev reference.
Set .pipe_close to mmio_close, matching how function_graph wires both callbacks to the same handler.
Note, if the trace_pipe is read to completion, it will clean up the resources, but if one were to run:
# head -n 1 /sys/kernel/tracing/trace_pipe VERSION 20070824
Over and over again, it would trigger a massive leak.
--- kernel/trace/trace_mmiotrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_mmiotrace.c +1 -1
@@ -109,7 +109,6 @@ static void mmio_pipe_open(struct trace_ iter->private = hiter; } -/* XXX: This is not called when the pipe is closed! */ static void mmio_close (struct trace_iterator *iter) {struct header_iter *hiter = iter->private;
@@ -279,6 +278,7 @@ static struct tracer mmio_tracer __read_ .start = mmio_trace_start, .pipe_open = mmio_pipe_open, .close = mmio_close, + .pipe_close = mmio_close, .read = mmio_read, .print_line = mmio_print_line, .noboot = true,
Greg Kroah-Hartman
maintainerPATCH 193/456 tracing/eprobe: Fix exact system name matching in eprobe_dyn_event_match()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit f418d68d71fd4a0a9cef92377bc8c4c3334b5b53 upstream.
eprobe_dyn_event_match() checks if the target event system in argv[0] matches ep->event_system using strncmp(ep->event_system, argv[0], len). However, if ep->event_system is longer than len (e.g. "eprobes" vs "ep/event"), strncmp() still returns 0 because the first len characters match.
Check that ep->event_system[len] is '\0' to ensure exact system name matching.
--- kernel/trace/trace_eprobe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/trace/trace_eprobe.c +2 -1
@@ -159,7 +159,8 @@ static bool eprobe_dyn_event_match(constif (!slash)return false; -if (strncmp (ep->event_system, argv[0 ], slash - argv[0 ])) +if (strncmp (ep->event_system, argv[0 ], slash - argv[0 ]) || + ep->event_system[slash - argv[0 ]] !='\0' )return false;if (strcmp (ep->event_name, slash +1 ))return false;
Greg Kroah-Hartman
maintainerPATCH 194/456 tracing/probes: Avoid temporary buffer truncation in trace_probe_match_command_args()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit 15f197856d68882af9416fc97516bb55079b7677 upstream.
In trace_probe_match_command_args(), a stack buffer buf[MAX_ARGSTR_LEN + 1] (256 bytes) is used to format "<name>=<comm>". However, since name can be up to 32 bytes (MAX_ARG_NAME_LEN) and comm up to 255 bytes (MAX_ARGSTR_LEN), the formatted string can exceed 256 bytes and get truncated by snprintf(), causing spurious argument matching failures.
Instead of formatting into a temporary buffer on stack, compare the argument name, the '=' delimiter, and the comm expression directly.
--- kernel/trace/trace_probe.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
kernel/trace/trace_probe.c +5 -4
@@ -1212,16 +1212,17 @@ int trace_probe_compare_arg_type(structbool trace_probe_match_command_args (struct trace_probe *tp,int argc,const char **argv) { -char buf[MAX_ARGSTR_LEN +1 ];int i;if (tp->nr_args < argc)return false;for (i =0 ; i < argc; i++) { -snprintf (buf,sizeof (buf),"%s=%s" , - tp->args[i].name, tp->args[i].comm); -if (strcmp (buf, argv[i])) +int len =strlen (tp->args[i].name); + +if (strncmp (argv[i], tp->args[i].name, len) || + argv[i][len] !='=' || +strcmp (argv[i] + len +1 , tp->args[i].comm))return false; }return true;
Greg Kroah-Hartman
maintainerPATCH 195/456 tracing/probes: Fix potential underflow in LEN_OR_ZERO macro
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit 8ce20bfba48902e1382187cd1a852f7cf3a1e739 upstream.
In __set_print_fmt(), LEN_OR_ZERO is defined as (len ? len - pos : 0). If len is non-zero but smaller than pos, len - pos evaluates to a negative integer. When passed as a size argument to snprintf(), this negative value is cast to a large unsigned size_t, bypassing buffer size limits.
Ensure len > pos before subtracting to avoid integer underflow.
--- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_probe.c +1 -1
@@ -896,7 +896,7 @@ int traceprobe_update_arg(struct probe_a }/* When len=0, we just calculate the needed length */ -#define LEN_OR_ZERO (len ? len - pos : 0) +#define LEN_OR_ZERO (len > pos ? len - pos : 0)static int __set_print_fmt (struct trace_probe *tp,char *buf,int len,enum probe_print_type ptype) {
Greg Kroah-Hartman
maintainerPATCH 196/456 tracing/probes: Prevent out-of-bounds write in __trace_probe_log_err()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
commit a9d6fb284039a5d3858a1d9f9a0d7e46cfb7c2d4 upstream.
If trace_probe_log.argc is 0 in __trace_probe_log_err(), the loop constructing the command string will not execute and p will remain equal to command. Writing to *(p - 1) will cause an out-of-bounds access before command. This should not happen, but better to be treated.
Reject if trace_probe_log.argc is 0.
--- kernel/trace/trace_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/trace/trace_probe.c +1 -1
@@ -176,7 +176,7 @@ void __trace_probe_log_err(int offset, ilockdep_assert_held (&dyn_event_ops_mutex); -if (!trace_probe_log.argv) +if (!trace_probe_log.argv || !trace_probe_log.argc)return ;/* Recalculate the length and allocate buffer */
Greg Kroah-Hartman
maintainerPATCH 197/456 arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Will Deacon <[email protected]>
commit e057b94772328221405b067c3a85fe479b915dc8 upstream.
When seccomp support was originally added to arm64 in a1ae65b21941 ("arm64: add seccomp support"), seccomp was erroneously called _before_ the ptrace syscall-enter-stop and therefore the tracer could trivially manipulate the syscall register state after the seccomp check had passed. This was subsequently fixed in a5cd110cb836 ("arm64/ptrace: run seccomp after ptrace") by moving the seccomp check after the tracer has run. Unfortunately, a decade later, that fix has been reported to be incomplete.
On arm64, both the first argument to a syscall and its eventual return value are allocated to register x0. In order to facilitate syscall restarting and querying of syscall arguments on the syscall exit path, the original value of x0 is stashed in 'struct pt_regs::orig_x0' early during the syscall entry path and is returned for the first argument by syscall_get_arguments(). Unlike 32-bit Arm, this stashed value is not directly exposed via ptrace() and so changes to register x0 made by the tracer on a syscall-enter-stop are not reflected in 'orig_x0'. This means that seccomp, syscall tracepoints and audit can observe a stale value for the register compared to the argument that will be observed by the actual syscall.
Re-sync 'orig_x0' from x0 on the syscall entry path following a potential ptrace stop (i.e. PTRACE_EVENTMSG_SYSCALL_ENTRY or SECCOMP_RET_TRACE). This behaviour is limited to native tasks (because compat tasks expose 'orig_r0' to ptrace) where the syscall is not being skipped (because x0 is updated to hold the return value of -ENOSYS in that case).
--- arch/arm64/kernel/ptrace.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
arch/arm64/kernel/ptrace.c +29 -0
@@ -1844,6 +1844,21 @@ static void tracehook_report_syscall(str } } +static void update_syscall_orig_x0_after_ptrace (struct pt_regs *regs) +{ +/* +* Keep orig_x0 authoritative so that seccomp (via +* syscall_get_arguments()), audit and the restart path all see the same +* first argument the syscall is dispatched with, even if it has been +* updated by a tracer. Skip this for NO_SYSCALL (set either by the user +* or the tracer), as regs[0] holds the return value (see the comment in +* el0_svc_common()) and can be unwound using syscall_rollback(). +* For compat tasks, orig_r0 is provided directly through GPR index 17. +*/ +if (!is_compat_task () && regs->syscallno !=NO_SYSCALL ) + regs->orig_x0 = regs->regs[0 ]; +} +int syscall_trace_enter (struct pt_regs *regs) {unsigned long flags =READ_ONCE (current_thread_info ()->flags);
@@ -1852,12 +1867,26 @@ int syscall_trace_enter(struct pt_regs *tracehook_report_syscall (regs,PTRACE_SYSCALL_ENTER );if (flags & _TIF_SYSCALL_EMU)return NO_SYSCALL ; + +/* +* Ensure ptrace changes to x0 during a regular +* syscall-enter-stop (PTRACE_SYSCALL) are visible to +* subsequent seccomp checks, tracepoints and audit. +*/ +update_syscall_orig_x0_after_ptrace (regs); }/* Do the secure computing after ptrace; failures should be fast. */ if (secure_computing () ==-1 )return NO_SYSCALL ; +/* +* Ensure tracer changes to x0 during seccomp ptrace exit +* processing (SECCOMP_RET_TRACE) are visible to tracepoints and +* audit. +*/ +update_syscall_orig_x0_after_ptrace (regs); +if (test_thread_flag (TIF_SYSCALL_TRACEPOINT ))trace_sys_enter (regs, regs->syscallno);
Greg Kroah-Hartman
maintainerPATCH 198/456 Revert "arm64: syscall: Ensure saved x0 is kept in-sync with tracer updates"
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Will Deacon <[email protected]>
commit 26b483d52417253d88a3a01262ac85914a7aec8e upstream.
This reverts commit e057b94772328221405b067c3a85fe479b915dc8.
Sashiko points out that updating 'orig_x0' after secure_computing() has returned is too late to handle the case where a seccomp filter is re-evaluated after initially returning SECCOMP_RET_TRACE. This means that a tracer can manipulate the first argument of the syscall behind seccomp's back.
For now, revert the initial fix and we'll have another crack at it soon. Since the incorrect fix was cc'd to stable, do the same here with an appropriate fixes tag.
--- arch/arm64/kernel/ptrace.c | 29 ----------------------------- 1 file changed, 29 deletions(-)
arch/arm64/kernel/ptrace.c +0 -29
@@ -1844,21 +1844,6 @@ static void tracehook_report_syscall(str } } -static void update_syscall_orig_x0_after_ptrace (struct pt_regs *regs) -{ -/* -* Keep orig_x0 authoritative so that seccomp (via -* syscall_get_arguments()), audit and the restart path all see the same -* first argument the syscall is dispatched with, even if it has been -* updated by a tracer. Skip this for NO_SYSCALL (set either by the user -* or the tracer), as regs[0] holds the return value (see the comment in -* el0_svc_common()) and can be unwound using syscall_rollback(). -* For compat tasks, orig_r0 is provided directly through GPR index 17. -*/ -if (!is_compat_task () && regs->syscallno !=NO_SYSCALL ) - regs->orig_x0 = regs->regs[0 ]; -} -int syscall_trace_enter (struct pt_regs *regs) {unsigned long flags =READ_ONCE (current_thread_info ()->flags);
@@ -1867,26 +1852,12 @@ int syscall_trace_enter(struct pt_regs *tracehook_report_syscall (regs,PTRACE_SYSCALL_ENTER );if (flags & _TIF_SYSCALL_EMU)return NO_SYSCALL ; - -/* -* Ensure ptrace changes to x0 during a regular -* syscall-enter-stop (PTRACE_SYSCALL) are visible to -* subsequent seccomp checks, tracepoints and audit. -*/ -update_syscall_orig_x0_after_ptrace (regs); }/* Do the secure computing after ptrace; failures should be fast. */ if (secure_computing () ==-1 )return NO_SYSCALL ; -/* -* Ensure tracer changes to x0 during seccomp ptrace exit -* processing (SECCOMP_RET_TRACE) are visible to tracepoints and -* audit. -*/ -update_syscall_orig_x0_after_ptrace (regs); -if (test_thread_flag (TIF_SYSCALL_TRACEPOINT ))trace_sys_enter (regs, regs->syscallno);
Greg Kroah-Hartman
maintainerPATCH 199/456 mptcp: only set DATA_FIN when a mapping is present
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit b2ff91b752b0d85e8815e7f44fd85205c4268094 upstream.
mptcp_get_options() clears only the status group of struct mptcp_options_received; data_seq, subflow_seq and data_len are filled in by mptcp_parse_option() exclusively inside the DSS mapping block, which runs only when the DSS M (mapping present) bit is set.
A peer can send a DSS option with the DATA_FIN flag set but the mapping bit clear. The parser then records mp_opt->data_fin while leaving data_len and data_seq uninitialized. For a zero-length segment mptcp_incoming_options() evaluates
if (mp_opt.data_fin && mp_opt.data_len == 1 && mptcp_update_rcv_data_fin(msk, mp_opt.data_seq, mp_opt.dsn64))
which reads the uninitialized data_len and data_seq; KMSAN reports an uninit-value in mptcp_incoming_options(). The stale data_seq can also be fed into the receive-side DATA_FIN sequence tracking.
Record the DATA_FIN flag only when the DSS option carries a mapping, so data_fin is never set without data_seq and data_len also being present. data_fin is part of the status group that mptcp_get_options() clears up front, so on the no-map path it stays zero and the zero-length DATA_FIN branch is simply skipped. A DATA_FIN is always transmitted together with a mapping (mptcp_write_data_fin() sets use_map along with data_seq and data_len), so legitimate DATA_FIN handling is unaffected.
Move the pr_debug() that logs the parsed DSS flags below the mapping block, so it reports the final data_fin value instead of the stale one it would otherwise print before the assignment.
--- net/mptcp/options.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
net/mptcp/options.c +6 -6
@@ -152,17 +152,11 @@ static void mptcp_parse_option(const str ptr++; flags = (*ptr++) &MPTCP_DSS_FLAG_MASK ; - mp_opt->data_fin = (flags &MPTCP_DSS_DATA_FIN ) !=0 ; mp_opt->dsn64 = (flags &MPTCP_DSS_DSN64 ) !=0 ; mp_opt->use_map = (flags &MPTCP_DSS_HAS_MAP ) !=0 ; mp_opt->ack64 = (flags &MPTCP_DSS_ACK64 ) !=0 ; mp_opt->use_ack = (flags &MPTCP_DSS_HAS_ACK ); -pr_debug ("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n" , - mp_opt->data_fin, mp_opt->dsn64, - mp_opt->use_map, mp_opt->ack64, - mp_opt->use_ack); - expected_opsize =TCPOLEN_MPTCP_DSS_BASE ;if (mp_opt->use_ack) {
@@ -173,12 +167,18 @@ static void mptcp_parse_option(const str }if (mp_opt->use_map) { + mp_opt->data_fin = (flags &MPTCP_DSS_DATA_FIN ) !=0 ;if (mp_opt->dsn64) expected_opsize +=TCPOLEN_MPTCP_DSS_MAP64 ;else expected_opsize +=TCPOLEN_MPTCP_DSS_MAP32 ; } +pr_debug ("data_fin=%d dsn64=%d use_map=%d ack64=%d use_ack=%d\n" , + mp_opt->data_fin, mp_opt->dsn64, + mp_opt->use_map, mp_opt->ack64, + mp_opt->use_ack); +/* Always parse any csum presence combination, we will enforce * RFC 8684 Section 3.3.0 checks later in subflow_data_ready */
Greg Kroah-Hartman
maintainerPATCH 200/456 sctp: dont free the ASCONFs own transport in DEL-IP processing
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jun Yang <[email protected]>
commit 9b2854f86f0b56e9027d68e7a3fc909d1a9b566f upstream.
sctp_process_asconf() caches the transport the ASCONF chunk is processed against in asconf->transport (== chunk->transport, set once in sctp_rcv()). For an ASCONF located through its Address Parameter by __sctp_rcv_asconf_lookup(), that cached transport corresponds to the Address Parameter, which need not be the packet's source address.
sctp_process_asconf_param() rejects a DEL-IP for the packet source address (ADDIP D8, SCTP_ERROR_DEL_SRC_IP), but nothing protects asconf->transport. A single ASCONF can therefore carry, in order:
[Address Parameter L] [DEL-IP L] [DEL-IP 0.0.0.0]
where L differs from the source. The DEL-IP for L passes the D8 check and calls sctp_assoc_rm_peer() on the transport that asconf->transport still points at, freeing it (RCU-deferred). The following wildcard DEL-IP then reuses the now-dangling asconf->transport in sctp_assoc_set_primary() and sctp_assoc_del_nonprimary_peers(): set_primary() dereferences the freed transport (->ipaddr, ->state) and plants the dangling pointer into asoc->peer.primary_path / active_path, and del_nonprimary_peers(), keeping only the pointer that is no longer on the list, removes every real transport, leaving the association with a transport_count of 0 and primary_path/active_path pointing at freed memory.
Reject a DEL-IP that targets the transport the ASCONF is being processed against, mirroring the existing source-address guard, so the wildcard branch can never reuse a freed transport.
--- net/sctp/sm_make_chunk.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/sctp/sm_make_chunk.c +6 -0
@@ -3179,6 +3179,12 @@ static __be16 sctp_process_asconf_param(if (!peer)return SCTP_ERROR_DNS_FAILED ; +/* Don't free asconf->transport; a later wildcard DEL-IP +* parameter reuses it. +*/ +if (peer == asconf->transport) +return SCTP_ERROR_REQ_REFUSED ; +sctp_assoc_rm_peer (asoc, peer);break ;case SCTP_PARAM_SET_PRIMARY :
Greg Kroah-Hartman
maintainerPATCH 201/456 ceph: fix pre-auth out-of-bounds read on snaptrace in ceph_handle_caps()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02 upstream.
ceph_handle_caps() reads snap_trace_len from the wire-format ceph_mds_caps header and uses it unconditionally to build a fake end pointer (snaptrace + snaptrace_len) that is later handed to ceph_update_snap_trace() in the CEPH_CAP_OP_IMPORT case:
snaptrace = h + 1;
snaptrace_len = le32_to_cpu(h->snap_trace_len);
p = snaptrace + snaptrace_len;
...
case CEPH_CAP_OP_IMPORT:
if (snaptrace_len) {
...
if (ceph_update_snap_trace(mdsc, snaptrace,
snaptrace + snaptrace_len,
false, &realm)) { ... }ceph_update_snap_trace() then decodes a struct ceph_mds_snap_realm from snaptrace using ceph_decode_need(&p, e, sizeof(*ri), bad) with the attacker-supplied fake end e == snaptrace + snaptrace_len. With snaptrace_len == 0xFFFFFFFF the bound check is trivially satisfied, ri = p reads sizeof(struct ceph_mds_snap_realm) past the legitimate msg->front buffer, and ri->num_snaps / ri->num_prior_parent_snaps then drive further out-of-bounds reads of the encoded snap arrays.
The eleven msg_version >= 2 .. msg_version >= 12 decoder blocks above the op switch each catch this OOB through their ceph_decode_*_safe() / ceph_decode_need() helpers, but they sit behind a hdr.version-gated if, so a malicious or compromised MDS that sets msg->hdr.version = 1 reaches the IMPORT path with no version-gated decoder having validated snap_trace_len. The shape has been present since ceph_handle_caps() was introduced.
Validate snap_trace_len against the message front buffer before consuming it, using the canonical ceph_decode_need() / ceph_has_room() helper. The helper bounds the length with subtraction (n <= end - p, guarded by end >= p) rather than pointer addition, so it is wrap-safe for the attacker-controlled u32 length on 32-bit builds where p + snap_trace_len could overflow the address space. This matches the rest of the ceph decode path (e.g. the pool_ns_len check a few lines below), and the existing goto bad cleanup already covers this exit path.
--- fs/ceph/caps.c | 1 + 1 file changed, 1 insertion(+)
fs/ceph/caps.c +1 -0
@@ -4067,6 +4067,7 @@ void ceph_handle_caps(struct ceph_mds_se snaptrace = h +1 ; snaptrace_len =le32_to_cpu (h->snap_trace_len); +ceph_decode_need (&snaptrace, end, snaptrace_len, bad); p = snaptrace + snaptrace_len;if (msg_version >=2 ) {
Greg Kroah-Hartman
maintainerPATCH 202/456 libceph: bound get_version reply decode to front len
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Douya Le <[email protected]>
commit d3c32939fa0e3ee9b883b9a0fd1972c5c444e3d0 upstream.
handle_get_version_reply() uses msg->front_alloc_len as the decode boundary for MON_GET_VERSION_REPLY. That is the size of the reused reply buffer, not the number of bytes actually received.
A truncated reply can therefore pass ceph_decode_need() and decode the second u64 from stale tail bytes left in the buffer by an earlier message, causing an uninitialized memory read.
Use msg->front.iov_len as the receive-side decode boundary, matching other libceph reply handlers and limiting decoding to the bytes that were actually read from the wire.
--- net/ceph/mon_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ceph/mon_client.c +1 -1
@@ -821,7 +821,7 @@ static void handle_get_version_reply(strstruct ceph_mon_generic_request *req;u64 tid =le64_to_cpu (msg->hdr.tid);void *p = msg->front.iov_base; -void *end = p + msg->front_alloc_len; +void *const end = p + msg->front.iov_len;u64 handle;dout ("%s msg %p tid %llu\n" , __func__, msg, tid);
Greg Kroah-Hartman
maintainerPATCH 203/456 libceph: Fix multiplication overflow in decode_new_up_state_weight()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raphael Zimmer <[email protected]>
commit 98917a499ec7064c14fc56d180a4fd636fc2784c upstream.
If a message of type CEPH_MSG_OSD_MAP contains a (maliciously) corrupted osdmap, out-of-bounds memory accesses may occur in decode_new_up_state_weight(). This happens because the bounds check for the new_state part is based on calculating its length depending on a len value read from the incoming message. This calculation may overflow leading to an incorrect bounds check. Subsequently, out-of-bounds reads may occur when decoding this part.
This patch switches the multiplication to use check_mul_overflow() to abort processing the osdmap if an overflow occurred. Therefore, osdmaps/messages containing large values for len that result in a multiplication overflow are treated as invalid.
[ idryomov: rename new_state_len -> new_state_item_size, formatting ]
--- net/ceph/osdmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/ceph/osdmap.c +4 -1
@@ -1830,6 +1830,8 @@ static int decode_new_up_state_weight(vovoid *new_up_client;void *new_state;void *new_weight_end; +const u32 new_state_item_size = +sizeof (u32) + (struct_v >=5 ?sizeof (u32) :sizeof (u8));u32 len;int ret;int i;
@@ -1850,7 +1852,8 @@ static int decode_new_up_state_weight(vo new_state = *p;ceph_decode_32_safe (p, end, len, e_inval); - len *=sizeof (u32) + (struct_v >=5 ?sizeof (u32) :sizeof (u8)); +if (check_mul_overflow (len, new_state_item_size, &len)) + goto e_inval;ceph_decode_need (p, end, len, e_inval); *p += len;
Greg Kroah-Hartman
maintainerPATCH 204/456 libceph: guard missing CRUSH type name lookup
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Zhang <[email protected]>
commit bbeae12fda3384a90fbebc8a19ba9d33f85b5361 upstream.
Localized read selection can walk a parent bucket whose name exists in the CRUSH map while its type has no matching entry in type_names. get_immediate_parent() then dereferences a NULL type_cn and passes an invalid pointer into strcmp(), causing a null-ptr-deref.
Skip such malformed parent buckets unless both the bucket name and type name metadata are present. This keeps malformed hierarchy data from crashing locality lookup and safely falls back to "not local".
[ idryomov: add WARN_ON_ONCE ]
--- net/ceph/osdmap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
net/ceph/osdmap.c +4 -1
@@ -3040,8 +3040,11 @@ static int get_immediate_parent(struct cif (b->items[j] != id)continue ; - *parent_type_id = b->type; type_cn =lookup_crush_name (&c->type_names, b->type); +if (WARN_ON_ONCE (!type_cn)) +continue ; + + *parent_type_id = b->type; parent_loc->cl_type_name = type_cn->cn_name; parent_loc->cl_name = cn->cn_name;return b->id;
Greg Kroah-Hartman
maintainerPATCH 205/456 libceph: refresh auth->authorizer_buf{,_len} after authorizer update
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shuangpeng Bai <[email protected]>
commit 937d61f86d377a3aa578adae7a3dfcecdddf9d89 upstream.
ceph_x_create_authorizer() caches au->buf->vec.iov_base and au->buf->vec.iov_len in struct ceph_auth_handshake. These cached values are then used by the messenger connect code when sending the authorizer.
ceph_x_update_authorizer() can rebuild the authorizer when a newer service ticket is available. If the rebuilt authorizer no longer fits in the existing buffer, ceph_x_build_authorizer() drops its reference to au->buf and allocates a new one. If this is the final reference, ceph_buffer_put() frees the old ceph_buffer and its vec.iov_base, but auth->authorizer_buf still points at that freed memory.
A subsequent msgr1 reconnect can therefore queue the stale pointer and trigger a KASAN slab-use-after-free in _copy_from_iter() while tcp_sendmsg() copies the authorizer.
Refresh auth->authorizer_buf and auth->authorizer_buf_len after a successful authorizer rebuild so the messenger sends the current buffer.
--- net/ceph/auth_x.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
net/ceph/auth_x.c +8 -1
@@ -781,9 +781,16 @@ static int ceph_x_update_authorizer( au = (struct ceph_x_authorizer *)auth->authorizer;if (au->secret_id < th->secret_id) { +int ret; +dout ("ceph_x_update_authorizer service %u secret %llu < %llu\n" , au->service, au->secret_id, th->secret_id); -return ceph_x_build_authorizer (ac, th, au); + ret =ceph_x_build_authorizer (ac, th, au); +if (ret) +return ret; + + auth->authorizer_buf = au->buf->vec.iov_base; + auth->authorizer_buf_len = au->buf->vec.iov_len; }return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 206/456 libceph: Reject monmaps advertising zero monitors
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raphael Zimmer <[email protected]>
commit 40480eee361ed9676b3f844d532ac28b47251634 upstream.
A message of type CEPH_MSG_MON_MAP contains a monmap that is sent from a monitor to the client. This monmap contains information about the existing monitors in the cluster. Currently, a monmap indicating that there are zero monitors in the cluster is treated as valid. However, it is impossible to have zero monitors in the cluster and still receive a valid monmap from a monitor. Therefore, such a monmap must be corrupted and should be treated as invalid. Furthermore, a monmap with a monitor count of zero can subsequently crash the client when attempting to open a session with a monitor in __open_session(). This happens because the "BUG_ON(monc->monmap->num_mon < 1)" assertion in pick_new_mon() is triggered.
This patch extends a check in ceph_monmap_decode() to also reject arriving mon_maps with num_mon == 0 rather than only with num_mon > CEPH_MAX_MON.
[ idryomov: drop "log output for unusual values of num_mon" part ]
--- net/ceph/mon_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ceph/mon_client.c +1 -1
@@ -114,7 +114,7 @@ static struct ceph_monmap *ceph_monmap_ddout ("%s fsid %pU epoch %u num_mon %u\n" , __func__, &fsid, epoch, num_mon); -if (num_mon >CEPH_MAX_MON ) +if (num_mon ==0 || num_mon >CEPH_MAX_MON ) goto e_inval; monmap =kmalloc (struct_size (monmap, mon_inst, num_mon),GFP_NOIO );
Greg Kroah-Hartman
maintainerPATCH 207/456 libceph: reject zero bucket types in crush_decode
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Douya Le <[email protected]>
commit 05f90284223381005d6bcddab3fda4a97f9c3401 upstream.
CRUSH bucket type 0 is reserved for devices. The mapper relies on that invariant and uses type 0 to identify leaf devices.
If crush_decode() accepts a bucket with type 0, a malformed CRUSH map can make the mapper treat a negative bucket ID as a device and pass it to is_out(), which then indexes the OSD weight array with a negative value.
Reject zero bucket types while decoding the CRUSH map so the invalid state never reaches the mapper.
--- net/ceph/osdmap.c | 2 ++ 1 file changed, 2 insertions(+)
net/ceph/osdmap.c +2 -0
@@ -504,6 +504,8 @@ static struct crush_map *crush_decode(voceph_decode_need (p, end,4 *sizeof (u32), bad); b->id =ceph_decode_32 (p); b->type =ceph_decode_16 (p); +if (b->type ==0 ) + goto bad; b->alg =ceph_decode_8 (p);if (b->alg != alg) { b->alg =0 ;
Greg Kroah-Hartman
maintainerPATCH 208/456 libceph: remove debugfs files before client teardown
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Douya Le <[email protected]>
commit e4c804726c4afce3ba648b982d564f6af2cfa328 upstream.
ceph_destroy_client() tears down the monitor client before removing the per-client debugfs files. A concurrent read of the monmap debugfs file can enter monmap_show() after ceph_monc_stop() has freed monc->monmap, triggering a use-after-free.
Remove the debugfs files before stopping the OSD and monitor clients. debugfs_remove() drains active handlers and prevents new accesses, so the debugfs callbacks can no longer race the rest of client teardown.
--- net/ceph/ceph_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
net/ceph/ceph_common.c +2 -2
@@ -783,13 +783,13 @@ void ceph_destroy_client(struct ceph_cliatomic_set (&client->msgr.stopping,1 ); +ceph_debugfs_client_cleanup (client); +/* unmount */ ceph_osdc_stop (&client->osdc);ceph_monc_stop (&client->monc);ceph_messenger_fini (&client->msgr); -ceph_debugfs_client_cleanup (client); -ceph_destroy_options (client->options);kfree (client);
Greg Kroah-Hartman
maintainerPATCH 209/456 binfmt_elf_fdpic: only honour the first PT_INTERP
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Brauner <[email protected]>
commit 3349ef6a366a61d631f6a263d12cea240957719d upstream.
The program header scan handles PT_INTERP from a switch nested in the scan loop, so its break leaves the switch and not the loop. A binary carrying more than one PT_INTERP runs the case again and overwrites both interpreter_name and interpreter. The previous name allocation leaks and so does the previous interpreter reference, along with the write denial open_exec() took on it. The denial is never released, so the file stays unwritable for as long as the system runs.
An unprivileged caller reaches this with a crafted binary and repeats it at will. binfmt_elf stops at the first PT_INTERP. Do the same here.
The flaw dates back to the driver's introduction in the pre-git history tree introduced in v2.6.11 by 91808d6ebe39 ("[PATCH] FRV: Add FDPIC ELF binary format driver").
--- fs/binfmt_elf_fdpic.c | 4 ++++ 1 file changed, 4 insertions(+)
fs/binfmt_elf_fdpic.c +4 -0
@@ -231,6 +231,10 @@ static int load_elf_fdpic_binary(structfor (i =0 ; i < exec_params.hdr.e_phnum; i++, phdr++) {switch (phdr->p_type) {case PT_INTERP : +/* elf ABI allows only one interpreter */ +if (interpreter_name) +continue ; + retval = -ENOMEM ;if (phdr->p_filesz >PATH_MAX ) goto error;
Greg Kroah-Hartman
maintainerPATCH 210/456 iommu/vt-d: Disallow SVA if page walk is not coherent
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Baolu <[email protected]>
commit 780dfed688622ea01be3c9c2c55eec2207f05e04 upstream.
Hardware implementations report Scalable-Mode Page-walk Coherency Support via the SMPWCS field in the extended capability register. If the hardware does not support page-walk coherency, a clflush is required every time the page table entries (which are walked by the IOMMU hardware) are updated.
In the SVA case, page tables are managed by the CPU mm core, not by the IOMMU driver. Because the IOMMU driver has no way of knowing whether the CPU page table management code has ensured coherency via clflush, the driver must deny SVA if the hardware does not support coherent paging.
--- drivers/iommu/intel/svm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/intel/svm.c +1 -1
@@ -175,7 +175,7 @@ static inline bool intel_svm_capable(strvoid intel_svm_check (struct intel_iommu *iommu) { -if (!pasid_supported (iommu)) +if (!pasid_supported (iommu) || !ecap_smpwc (iommu->ecap))return ; if (cpu_feature_enabled(X86_FEATURE_GBPAGES) &&
Greg Kroah-Hartman
maintainerPATCH 211/456 phonet: pep: fix use-after-free in pep_get_sb()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
commit 0f71f852a96af9685858ce59fda34ecbf85c283d upstream.
pep_get_sb() doesn't consider that pskb_may_pull() might have relocated the skb data, and continue to access the older pointer, causing UAF.
Reproduced under KASAN:
BUG: KASAN: slab-use-after-free in pep_get_sb+0x234/0x3b0 Read of size 1 at addr ff11000105510f50 by task repro/157 pep_get_sb+0x234/0x3b0 pipe_handler_do_rcv+0x5f7/0xa10 pep_do_rcv+0x203/0x410 __sk_receive_skb+0x471/0x4a0 phonet_rcv+0x5b3/0x6c0 __netif_receive_skb+0xcc/0x1d0
Refetch the header with skb_header_pointer() after pskb_may_pull(), so the possibly stale pointer is no longer dereferenced. There are better ways to solve this, but, this is the less instrusive one.
--- net/phonet/pep.c | 2 ++ 1 file changed, 2 insertions(+)
net/phonet/pep.c +2 -0
@@ -55,6 +55,8 @@ static unsigned char *pep_get_sb(struct ph =skb_header_pointer (skb,0 ,2 , &h);if (ph ==NULL || ph->sb_len <2 || !pskb_may_pull (skb, ph->sb_len))return NULL ; +/* pskb_may_pull() may have reallocated the head; refetch ph. */ + ph =skb_header_pointer (skb,0 ,2 , &h); ph->sb_len -=2 ; *ptype = ph->sb_type; *plen = ph->sb_len;
Greg Kroah-Hartman
maintainerPATCH 212/456 vxlan: require CAP_NET_ADMIN in the device netns for changelink
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 3a61bd9637f3d929aa846e4eb3d98b48c26fcb0e upstream.
A tunnel changelink() operates on at most two netns, dev_net(dev) and the sticky underlay netns vxlan->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in vxlan->net can rewrite a vxlan device whose underlay lives in vxlan->net.
vxlan_changelink() validates and applies the new configuration against vxlan->net (vxlan_config_validate(vxlan->net, ...)) and can reopen the underlay socket in that netns, so the same reasoning as the tunnel changelink series applies here.
Gate vxlan_changelink() with rtnl_dev_link_net_capable(), at the top of the op before any attribute is parsed, matching ipgre_changelink() and the rest of the "require CAP_NET_ADMIN in the device netns for changelink" series.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- drivers/net/vxlan/vxlan_core.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/vxlan/vxlan_core.c +3 -0
@@ -4259,6 +4259,9 @@ static int vxlan_changelink(struct net_dstruct vxlan_rdst *dst;int err; +if (!rtnl_dev_link_net_capable (dev, vxlan->net)) +return -EPERM ; + dst = &vxlan->default_dst; err =vxlan_nl2conf (tb, data, dev, &conf, true, extack);if (err)
Greg Kroah-Hartman
maintainerPATCH 213/456 net: slip: serialize receive against buffer reallocation
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sungmin Kang <[email protected]>
commit ee7f9bb9320add61f7b367d7e6cd55e3a3a4d65d upstream.
sl_realloc_bufs() replaces rbuff and updates buffsize while holding sl->lock. slip_receive_buf() reads those fields and writes through rbuff without holding the lock.
An MTU change can therefore race with receive processing. An MTU shrink can expose the new smaller rbuff with the old larger bound, causing an out-of-bounds write. A receive callback which already loaded the old rbuff can instead continue writing after that buffer has been freed.
Serialize receive processing with sl_realloc_bufs() by holding sl->lock while consuming each receive batch.
--- drivers/net/slip/slip.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/slip/slip.c +4 -0
@@ -693,6 +693,8 @@ static void slip_receive_buf(struct tty_if (!sl || sl->magic !=SLIP_MAGIC || !netif_running (sl->dev))return ; +spin_lock_bh (&sl->lock); +/* Read the characters out of the buffer */ while (count--) {if (fp && *fp++) {
@@ -708,6 +710,8 @@ static void slip_receive_buf(struct tty_#endif slip_unesc (sl, *cp++); } + +spin_unlock_bh (&sl->lock); }/************************************
Greg Kroah-Hartman
maintainerPATCH 214/456 geneve: require CAP_NET_ADMIN in the device netns for changelink
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit 8efb8f8bbb353b8f2fdf4f37534c6d96c9f69e01 upstream.
A tunnel changelink() operates on at most two netns, dev_net(dev) and the sticky underlay netns geneve->net. They differ once the device is created in or moved to a netns other than the one the request runs in. The rtnl changelink path checks CAP_NET_ADMIN only against dev_net(dev), so a caller privileged there but not in geneve->net can rewrite a geneve device whose underlay lives in geneve->net.
geneve_changelink() applies the new configuration against geneve->net:geneve_link_config() and the geneve_quiesce()/geneve_unquiesce() pair reopen the underlay sockets in that netns (geneve_sock_add() uses geneve->net), so the same reasoning as the tunnel changelink series applies here.
Gate geneve_changelink() with rtnl_dev_link_net_capable(), at the top of the op before any attribute is parsed, matching ipgre_changelink() and the rest of the "require CAP_NET_ADMIN in the device netns for changelink" series.
Found by 0sec automated security-research tooling (https://0sec.ai).
--- drivers/net/geneve.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/geneve.c +3 -0
@@ -1754,6 +1754,9 @@ static int geneve_changelink(struct net_struct geneve_config cfg;int err; +if (!rtnl_dev_link_net_capable (dev, geneve->net)) +return -EPERM ; +/* If the geneve device is configured for metadata (or externally * controlled, for example, OVS), then nothing can be changed. */
Greg Kroah-Hartman
maintainerPATCH 215/456 net/af_iucv: fix NULL deref in afiucv_hs_callback_syn()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hidayath Khan <[email protected]>
commit 47a5116e56a6b6fe1e909f244e39cd0fc26ceee4 upstream.
afiucv_hs_callback_syn() allocates the child socket with GFP_ATOMIC. If the allocation fails, nsk is NULL.
The connection-refused path is entered when the listen state check fails, the accept backlog is full, or nsk is NULL. The code unconditionally calls iucv_sock_kill(nsk) in that path.
iucv_sock_kill() does not accept a NULL socket pointer and immediately dereferences sk via sock_flag(sk, SOCK_ZAPPED). When nsk is NULL, calling iucv_sock_kill(nsk) results in a NULL pointer dereference.
Only call iucv_sock_kill() when a child socket was successfully allocated.
--- net/iucv/af_iucv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
net/iucv/af_iucv.c +2 -1
@@ -1867,7 +1867,8 @@ static int afiucv_hs_callback_syn(structafiucv_swap_src_dest (skb); trans_hdr->flags =AF_IUCV_FLAG_SYN |AF_IUCV_FLAG_FIN ; err =dev_queue_xmit (skb); -iucv_sock_kill (nsk); +if (nsk) +iucv_sock_kill (nsk);bh_unlock_sock (sk); goto out; }
Greg Kroah-Hartman
maintainerPATCH 216/456 net/iucv: fix use-after-free of a severed iucv_path
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit be7cc4656eb1f54029610e82d1f0fdd3f9b5ec0a upstream.
af_iucv queues not-yet-received message notifications on iucv->message_q, each holding a raw pointer to the connection's iucv_path. When the peer severs the connection, iucv_sever_path() frees that path with iucv_path_free() but leaves the notifications queued. A later recvmsg() drains message_q via iucv_process_message_q() and hands the stale path to message_receive() -- a use-after-free of the freed iucv_path.
Drop the queued notifications when the path is severed; once the path is gone they can no longer be received. This also frees the notifications leaked when a socket is closed with messages still queued.
--- net/iucv/af_iucv.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
net/iucv/af_iucv.c +14 -0
@@ -335,6 +335,7 @@ static void iucv_sever_path(struct sockunsigned char user_data[16 ];struct iucv_sock *iucv =iucv_sk (sk);struct iucv_path *path = iucv->path; +struct sock_msg_q *p, *n;/* Whoever resets the path pointer, must sever and free it. */ if (xchg (&iucv->path,NULL )) {
@@ -346,6 +347,19 @@ static void iucv_sever_path(struct sock }else pr_iucv->path_sever (path,NULL );iucv_path_free (path); + +/* +* Message notifications queued on message_q still reference +* the now freed path; drop them, otherwise a later recvmsg() +* would pass the freed iucv_path to message_receive() via +* iucv_process_message_q(). +*/ +spin_lock_bh (&iucv->message_q.lock); +list_for_each_entry_safe (p, n, &iucv->message_q.list, list) { +list_del (&p->list); +kfree (p); + } +spin_unlock_bh (&iucv->message_q.lock); } }
Greg Kroah-Hartman
maintainerPATCH 217/456 net/x25: fix use-after-free in x25_kill_by_neigh()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Lee <[email protected]>
commit 5499e0602d2faafd42c580d25f615903c3fbe11b upstream.
x25_kill_by_neigh() walks the global X.25 socket list looking for sockets attached to a terminating neighbour. x25_list_lock protects list membership while the lookup is in progress, but it does not pin a socket's lifetime after the lock is dropped.
The function currently drops x25_list_lock before calling lock_sock(s). A concurrent close can run x25_release(), remove the same socket from x25_list, and drop the last socket reference in that window. The neighbour teardown path can then lock or inspect a freed struct sock/struct x25_sock.
Take sock_hold(s) while x25_list_lock still proves that the list entry is live, then drop the temporary reference after the socket has been locked, rechecked, and released. Recheck x25_sk(s)->neighbour after lock_sock(), because another path may have disconnected the socket before this path acquired the socket lock. Restart the list walk after each disconnect because the list lock was dropped and the previous iterator state may no longer be valid.
A QEMU/KASAN run against origin/master reproduced a slab-use-after-free in x25_kill_by_neigh().
--- net/x25/af_x25.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
net/x25/af_x25.c +6 -2
@@ -1769,15 +1769,19 @@ void x25_kill_by_neigh(struct x25_neigh {struct sock *s; +again:write_lock_bh (&x25_list_lock);sk_for_each (s, &x25_list) {if (x25_sk (s)->neighbour == nb) { +sock_hold (s);write_unlock_bh (&x25_list_lock);lock_sock (s); -x25_disconnect (s,ENETUNREACH ,0 ,0 ); +if (x25_sk (s)->neighbour == nb) +x25_disconnect (s,ENETUNREACH ,0 ,0 );release_sock (s); -write_lock_bh (&x25_list_lock); +sock_put (s); + goto again; } }write_unlock_bh (&x25_list_lock);
Greg Kroah-Hartman
maintainerPATCH 218/456 net: hip04: fix RX buffer leak on build_skb failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
commit 14fa65d10f5696b063a7d8d26e8291ea84a2c6ed upstream.
When build_skb() fails in hip04_rx_poll(), the driver jumps to the refill path without releasing the current RX buffer and its DMA mapping. Installing a replacement buffer then overwrites the slot references and leaks both resources.
Keep the current slot intact and return budget so NAPI retries the same buffer. Also free a newly allocated RX fragment when dma_map_single() fails.
This issue was found by an in-house static analysis tool.
--- drivers/net/ethernet/hisilicon/hip04_eth.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/net/ethernet/hisilicon/hip04_eth.c +8 -3
@@ -594,7 +594,11 @@ static int hip04_rx_poll(struct napi_str skb =build_skb (buf, priv->rx_buf_size);if (unlikely (!skb)) {net_dbg_ratelimited ("build_skb failed\n" ); - goto refill; +/* Retain the slot; return budget so NAPI retries this +* buffer. Refill would overwrite rx_buf[]/rx_phys[] +* and leak them. +*/ +return budget; } dma_unmap_single(priv->dev, priv->rx_phys[priv->rx_head],
@@ -622,14 +626,15 @@ static int hip04_rx_poll(struct napi_str rx++; } -refill: buf =netdev_alloc_frag (priv->rx_buf_size);if (!buf) goto done; phys =dma_map_single (priv->dev, buf,RX_BUF_SIZE ,DMA_FROM_DEVICE ); -if (dma_mapping_error (priv->dev, phys)) +if (dma_mapping_error (priv->dev, phys)) { +skb_free_frag (buf); goto done; + } priv->rx_buf[priv->rx_head] = buf; priv->rx_phys[priv->rx_head] = phys;hip04_set_recv_desc (priv, phys);
Greg Kroah-Hartman
maintainerPATCH 219/456 proc: Fix broken error paths for namespace links
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jann Horn <[email protected]>
commit 425224c2d700391729be7fe6929a88ef4e2d7a4e upstream.
Don't return the return value of down_read_killable() (0) when a ptrace access check fails, return -EACCES as intended.
--- fs/proc/namespaces.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/proc/namespaces.c +3 -1
@@ -46,7 +46,7 @@ static const char *proc_ns_get_link(struconst struct proc_ns_operations *ns_ops =PROC_I (inode)->ns_ops;struct task_struct *task;struct path ns_path; -int error = -EACCES ; +int error;if (!dentry)return ERR_PTR (-ECHILD );
@@ -59,6 +59,7 @@ static const char *proc_ns_get_link(struif (error) goto out_put_task; + error = -EACCES ;if (!ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) goto out;
@@ -90,6 +91,7 @@ static int proc_ns_readlink(struct dentrif (res) goto out_put_task; + res = -EACCES ;if (ptrace_may_access (task,PTRACE_MODE_READ_FSCREDS )) { res =ns_get_name (name,sizeof (name), task, ns_ops);if (res >=0 )
Greg Kroah-Hartman
maintainerPATCH 220/456 rbd: Reset positive result codes to zero in object map update path
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Raphael Zimmer <[email protected]>
commit a6c4250b81bd30beae94e1b7a4b26fa1193ad2e4 upstream.
In a reply message to an RBD request, a positive result code indicates a data payload, which is not allowed for writes. While rbd_osd_req_callback() already resets a positive result code for writes to zero, rbd_object_map_callback() does not. This allows a corrupted reply to an object map update to trigger the rbd_assert(*result < 0) in __rbd_obj_handle_request(). This happens, because rbd_object_map_callback() calls rbd_obj_handle_request() -> __rbd_obj_handle_request() and passes this positive result code. From __rbd_obj_handle_request(), rbd_obj_advance_write() is called, which leaves the positive result code unchanged and returns true. Therefore, the if(done && *result) branch is executed in __rbd_obj_handle_request() and the assertion triggers.
This patch fixes the issue by adjusting the logic in the rbd_object_map_callback() path. A positive result code for an object map update is now reset to zero (similar to rbd_osd_req_callback()), and the message is subsequently handled the same way as if the result code was zero from the beginning. Additionally, a WARN_ON_ONCE() is added for this case.
--- drivers/block/rbd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/block/rbd.c +6 -1
@@ -1958,10 +1958,15 @@ static int rbd_object_map_update_finish(bool has_current_state;void *p; -if (osd_req->r_result) +if (osd_req->r_result <0 )return osd_req->r_result;/* +* Writes aren't allowed to return a data payload. +*/ +WARN_ON_ONCE (osd_req->r_result >0 ); + +/* * Nothing to do for a snapshot object map. */ if (osd_req->r_num_ops ==1 )
Greg Kroah-Hartman
maintainerPATCH 221/456 ice: use READ_ONCE() to access cached PHC time
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sergey Temerkhanov <[email protected]>
commit 2915681b89f817677ab9f1166d95b595bc144f5f upstream.
ptp.cached_phc_time is a 64-bit value updated by a periodic work item on one CPU and read locklessly on another. On 32-bit or non-atomic architectures this can result in a torn read. Use READ_ONCE() to enforce a single atomic load.
--- drivers/net/ethernet/intel/ice/ice_ptp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/intel/ice/ice_ptp.c +1 -1
@@ -391,7 +391,7 @@ static u64 ice_ptp_extend_40b_ts(struct {const u64 mask =GENMASK_ULL (31 ,0 ); -return ice_ptp_extend_32b_ts (pf->ptp.cached_phc_time, +return ice_ptp_extend_32b_ts (READ_ONCE (pf->ptp.cached_phc_time), (in_tstamp >>8 ) & mask); }
Greg Kroah-Hartman
maintainerPATCH 222/456 ila: reload IPv6 header after pskb_may_pull in checksum adjust
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 92d3817649df2b0b6a008a686c8275c88d7ef594 upstream.
ila_csum_adjust_transport() caches ip6h = ipv6_hdr(skb) before calling pskb_may_pull(). On a non-linear skb whose transport header sits in a page fragment, pskb_may_pull() can call __pskb_pull_tail() / pskb_expand_head() and free the old skb head, leaving ip6h dangling; the following get_csum_diff(ip6h, p) then reads freed memory. ila_update_ipv6_locator() uses ip6h (and the iaddr derived from it) again after the csum-adjust call and additionally writes the new locator through that pointer.
Impact: a remote IPv6 packet routed through a configured ILA csum-adjust-transport route or receive-side mapping triggers a slab-use-after-free in ila_update_ipv6_locator() (KASAN). The route or mapping requires CAP_NET_ADMIN to configure, but trigger packets are unauthenticated once it exists.
Reload ip6h after each pskb_may_pull() in ila_csum_adjust_transport() before the csum-diff read. In ila_update_ipv6_locator() only the ILA_CSUM_ADJUST_TRANSPORT case pulls the skb, so reload ip6h and iaddr in that case alone before the destination-address write; the neutral-map modes never pull and keep their cached pointers.
--- net/ipv6/ila/ila_common.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
net/ipv6/ila/ila_common.c +12 -0
@@ -84,6 +84,7 @@ static void ila_csum_adjust_transport(ststruct tcphdr *th = (struct tcphdr *) (skb_network_header (skb) + nhoff); + ip6h =ipv6_hdr (skb); diff =get_csum_diff (ip6h, p);inet_proto_csum_replace_by_diff (&th->check, skb, diff, true, true);
@@ -95,6 +96,7 @@ static void ila_csum_adjust_transport(st (skb_network_header (skb) + nhoff);if (uh->check || skb->ip_summed ==CHECKSUM_PARTIAL ) { + ip6h =ipv6_hdr (skb); diff =get_csum_diff (ip6h, p);inet_proto_csum_replace_by_diff (&uh->check, skb, diff, true, true);
@@ -109,6 +111,7 @@ static void ila_csum_adjust_transport(ststruct icmp6hdr *ih = (struct icmp6hdr *) (skb_network_header (skb) + nhoff); + ip6h =ipv6_hdr (skb); diff =get_csum_diff (ip6h, p);inet_proto_csum_replace_by_diff (&ih->icmp6_cksum, skb, diff, true, true);
@@ -126,6 +129,15 @@ void ila_update_ipv6_locator(struct sk_bswitch (p->csum_mode) {case ILA_CSUM_ADJUST_TRANSPORT :ila_csum_adjust_transport (skb, p); +/* +* ila_csum_adjust_transport() calls pskb_may_pull(), which can +* reallocate the skb head and leave ip6h (and the iaddr derived +* from it) dangling; reload both before the write below. The +* other csum modes do not pull, so their cached pointers stay +* valid. +*/ + ip6h =ipv6_hdr (skb); + iaddr =ila_a2i (&ip6h->daddr);break ;case ILA_CSUM_NEUTRAL_MAP :if (sir2ila) {
Greg Kroah-Hartman
maintainerPATCH 223/456 mac802154: llsec: reject frames shorter than the authentication tag
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
commit fd3a3f28ed60c6af4b2a39933b151d6b27842c3b upstream.
llsec_do_decrypt_auth() computes the associated-data length for the AEAD request as
assoclen += datalen - authlen;
where datalen is the number of bytes after the MAC header and authlen (4, 8 or 16) is the length of the authentication tag. Nothing verifies that the frame actually carries at least authlen payload bytes. A secured frame whose payload is shorter than the tag makes datalen - authlen negative; assoclen is then passed to aead_request_set_ad() as an unsigned value close to 4 GiB, so crypto_aead_decrypt() walks far off the end of the scatterlist that only spans the real frame.
The frame is fully attacker-controlled and reaches this path from any IEEE 802.15.4 peer in radio range. Reject frames whose payload is shorter than the authentication tag before the subtraction.
Dynamically reproduced on a KASAN kernel as a general-protection-fault in the AEAD scatterwalk, and the fix confirmed.
--- net/mac802154/llsec.c | 5 +++++ 1 file changed, 5 insertions(+)
net/mac802154/llsec.c +5 -0
@@ -888,6 +888,11 @@ llsec_do_decrypt_auth(struct sk_buff *sk data =skb_mac_header (skb) + skb->mac_len; datalen =skb_tail_pointer (skb) - data; +if (datalen < authlen) { +kfree_sensitive (req); +return -EBADMSG ; + } +sg_init_one (&sg,skb_mac_header (skb), assoclen + datalen);if (!(hdr->sec.level &IEEE802154_SCF_SECLEVEL_ENC )) {
Greg Kroah-Hartman
maintainerPATCH 224/456 pppoe: reload header pointer after dev_hard_header()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Asim Viladi Oglu Manizada <[email protected]>
commit e9c238f6fe42fb1b4dba3a578277de32cb487937 upstream.
pppoe_sendmsg() saves a pointer to the PPPoE header before calling dev_hard_header(). Device header callbacks are allowed to reallocate the skb head, invalidating pointers into it.
This can happen when a send is blocked in copy_from_user() while the first non-Ethernet port is added to an empty team device. The team's delegated GRE header callback then expands the skb head. PPPoE subsequently writes six bytes through the stale pointer into the freed head.
Reload the PPPoE header through the skb's network-header offset after device header creation. pskb_expand_head() updates that offset when it relocates the head.
--- drivers/net/ppp/pppoe.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ppp/pppoe.c +1 -0
@@ -899,6 +899,7 @@ static int pppoe_sendmsg(struct socket *dev_hard_header (skb, dev,ETH_P_PPP_SES , po->pppoe_pa.remote,NULL , total_len); + ph =pppoe_hdr (skb);memcpy (ph, &hdr,sizeof (struct pppoe_hdr )); ph->length =htons (total_len);
Greg Kroah-Hartman
maintainerPATCH 225/456 tipc: clear sock->sk on the failed-insert path in tipc_sk_create()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daehyeon Ko <[email protected]>
commit ba0533fc163f905fe817cfabdf8ed4058da44800 upstream.
When tipc_sk_create() fails to insert the new socket (tipc_sk_insert() returns non-zero), its error path frees the sk with sk_free() but leaves sock->sk pointing at the freed object:
if (tipc_sk_insert(tsk)) {
sk_free(sk);
pr_warn("Socket create failed; port number exhausted\n");
return -EINVAL;
}This is harmless for plain socket(): the syscall layer clears sock->ops before releasing, so tipc_release() is never called. It is not harmless on the accept() path. tipc_accept() creates the pre-allocated child socket with tipc_sk_create(net, new_sock, 0, kern); on failure it leaves new_sock->sk dangling and new_sock->ops non-NULL, and do_accept() then fput()s the new file, so __sock_release() -> tipc_release() runs lock_sock(new_sock->sk) on the freed sk -- a use-after-free write of the sk_lock spinlock.
tipc_release() already guards this exact "failed accept() releases a pre-allocated child" case with "if (sk == NULL) return 0;", but the guard is bypassed because tipc_sk_create() left sock->sk non-NULL (dangling) rather than NULL.
Clear sock->sk on the failed-insert path so the existing tipc_release() NULL check fires and the use-after-free is avoided.
The tipc_sk_insert() failure is reached when the per-netns socket rhashtable hits its max_size (tsk_rht_params.max_size = 1048576, ~2M elements) -- i.e. once a netns holds ~2M TIPC sockets every insert returns -E2BIG.
BUG: KASAN: slab-use-after-free in lock_sock_nested (net/core/sock.c:3839) Write of size 8 at addr ffff8880047cdc38 by task init/1 lock_sock_nested (net/core/sock.c:3839) tipc_release (net/tipc/socket.c:638) __sock_release (net/socket.c:710) sock_close (net/socket.c:1501) __fput (fs/file_table.c:512) Allocated by task 1: sk_alloc (net/core/sock.c:2308) tipc_sk_create (net/tipc/socket.c:487) tipc_accept (net/tipc/socket.c:2744) do_accept (net/socket.c:2034) Freed by task 1: __sk_destruct (net/core/sock.c:2391) tipc_sk_create (net/tipc/socket.c:504) tipc_accept (net/tipc/socket.c:2744) do_accept (net/socket.c:2034)
--- net/tipc/socket.c | 1 + 1 file changed, 1 insertion(+)
net/tipc/socket.c +1 -0
@@ -503,6 +503,7 @@ static int tipc_sk_create(struct net *netipc_set_sk_state (sk,TIPC_OPEN );if (tipc_sk_insert (tsk)) {sk_free (sk); + sock->sk =NULL ;pr_warn ("Socket create failed; port number exhausted\n" );return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 226/456 drm/amdgpu/gfx10: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit d06c4173a7c38c7a39e98859f839ce714c7af2c9 upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +5 -8
@@ -3854,7 +3854,7 @@ static void gfx_v10_0_wait_reg_mem(struc WAIT_REG_MEM_ENGINE(eng_sel)));if (mem_space) -BUG_ON (addr0 &0x3 );/* Dword align */ +WARN_ON (addr0 &0x3 );/* Dword align */ amdgpu_ring_write (ring, addr0);amdgpu_ring_write (ring, addr1);amdgpu_ring_write (ring, ref);
@@ -8629,7 +8629,7 @@ static void gfx_v10_0_ring_emit_ib_gfx(s }amdgpu_ring_write (ring, header); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -8664,7 +8664,7 @@ static void gfx_v10_0_ring_emit_ib_compu }amdgpu_ring_write (ring,PACKET3 (PACKET3_INDIRECT_BUFFER ,2 )); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -8697,9 +8697,9 @@ static void gfx_v10_0_ring_emit_fence(st* aligned if only send 32bit data low (discard data high) */ if (write64bit) -BUG_ON (addr &0x7 ); +WARN_ON (addr &0x7 );else -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
@@ -8735,9 +8735,6 @@ static void gfx_v10_0_ring_emit_fence_ki {struct amdgpu_device *adev = ring->adev; -/* we only allocate 32bit for each seq wb address */ -BUG_ON (flags &AMDGPU_FENCE_FLAG_64BIT ); -/* write fence seq to the "addr" */ amdgpu_ring_write (ring,PACKET3 (PACKET3_WRITE_DATA ,3 )); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
Greg Kroah-Hartman
maintainerPATCH 227/456 drm/amdgpu/gfx8: drop unecessary BUG_ON()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 84a1a8a952ab4b8c23c5dd1f2eea4049cb4914f5 upstream.
There's no need to crash the kernel for this case.
--- drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 3 --- 1 file changed, 3 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +0 -3
@@ -6301,9 +6301,6 @@ static void gfx_v8_0_ring_emit_fence_comstatic void gfx_v8_0_ring_emit_fence_kiq (struct amdgpu_ring *ring,u64 addr,u64 seq,unsigned int flags) { -/* we only allocate 32bit for each seq wb address */ -BUG_ON (flags &AMDGPU_FENCE_FLAG_64BIT ); -/* write fence seq to the "addr" */ amdgpu_ring_write (ring,PACKET3 (PACKET3_WRITE_DATA ,3 )); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) |
Greg Kroah-Hartman
maintainerPATCH 228/456 drm/amdgpu/gfx9: replace BUG_ON() with WARN_ON()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alex Deucher <[email protected]>
commit 6302be10b521f5106ce01eb5a724b9e7945a5061 upstream.
There's no need to crash the kernel for these cases.
--- drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +5 -5
@@ -1052,7 +1052,7 @@ static void gfx_v9_0_wait_reg_mem(struct WAIT_REG_MEM_ENGINE(eng_sel)));if (mem_space) -BUG_ON (addr0 &0x3 );/* Dword align */ +WARN_ON (addr0 &0x3 );/* Dword align */ amdgpu_ring_write (ring, addr0);amdgpu_ring_write (ring, addr1);amdgpu_ring_write (ring, ref);
@@ -5395,7 +5395,7 @@ static void gfx_v9_0_ring_emit_ib_gfx(st }amdgpu_ring_write (ring, header); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -5430,7 +5430,7 @@ static void gfx_v9_0_ring_emit_ib_comput }amdgpu_ring_write (ring,PACKET3 (PACKET3_INDIRECT_BUFFER ,2 )); -BUG_ON (ib->gpu_addr &0x3 );/* Dword align */ +WARN_ON (ib->gpu_addr &0x3 );/* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) |
@@ -5464,9 +5464,9 @@ static void gfx_v9_0_ring_emit_fence(str* aligned if only send 32bit data low (discard data high) */ if (write64bit) -BUG_ON (addr &0x7 ); +WARN_ON (addr &0x7 );else -BUG_ON (addr &0x3 ); +WARN_ON (addr &0x3 );amdgpu_ring_write (ring,lower_32_bits (addr));amdgpu_ring_write (ring,upper_32_bits (addr));amdgpu_ring_write (ring,lower_32_bits (seq));
Greg Kroah-Hartman
maintainerPATCH 229/456 drm/amdgpu: fix division by zero with invalid uvd dimensions
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Boyuan Zhang <[email protected]>
commit 0c01c811be47e6b146552dd59bfedbea8f09b8f4 upstream.
When width or height is less than 16, width_in_mb or height_in_mb becomes 0, leading to fs_in_mb being 0. This causes a division by zero when calculating num_dpb_buffer in H264 and H264 Perf decode paths.
Add validation to reject frames with width < 16 or height < 16 before performing any calculations that depend on these values.
V2: Format change - move up all vaiable definitions.
V3: Use warn_once to avoid spam.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +8 -0
@@ -578,6 +578,14 @@ static int amdgpu_uvd_cs_msg_decode(struunsigned image_size, tmp, min_dpb_size, num_dpb_buffer;unsigned min_ctx_size = ~0 ; +/* Reject invalid dimensions to prevent division by zero */ +if (width <16 || height <16 ) { +dev_WARN_ONCE (adev->dev,1 , +"Invalid UVD decoding dimensions (%dx%d)!\n" , + width, height); +return -EINVAL ; + } + image_size = width * height; image_size += image_size /2 ; image_size =ALIGN (image_size,1024 );
Greg Kroah-Hartman
maintainerPATCH 230/456 drm/amdgpu: invoke pm_genpd_remove() before freeing genpd
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ce Sun <[email protected]>
commit 28c9b3c5dc35cc790d11e26ca3fc6e068be63998 upstream.
Call pm_genpd_remove() to unregister from global list prior to releasing acp_genpd memory, and clear the pointer after free.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +2 -0
@@ -466,7 +466,9 @@ static int acp_hw_fini(void *handle)mfd_remove_devices (adev->acp.parent);kfree (adev->acp.acp_res); +pm_genpd_remove (&adev->acp.acp_genpd->gpd);kfree (adev->acp.acp_genpd); + adev->acp.acp_genpd =NULL ;kfree (adev->acp.acp_cell);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 231/456 tipc: fix use-after-free of the discoverer in tipc_disc_rcv()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit 1579342d71133da7f00daa02c75cebec7372097b upstream.
bearer_disable() frees b->disc with tipc_disc_delete()'s plain kfree(), but tipc_disc_rcv() still dereferences b->disc in RX softirq under rcu_read_lock() (tipc_udp_recv -> tipc_rcv -> tipc_disc_rcv).
L2 bearers are safe thanks to the synchronize_net() in tipc_disable_l2_media(), but the UDP bearer defers that call to the cleanup_bearer() workqueue, so the discoverer is freed with no grace period:
BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149) Read of size 8 at addr ffff88802348b728 by task poc_tipc/184 <IRQ> tipc_disc_rcv (net/tipc/discover.c:149) tipc_rcv (net/tipc/node.c:2126) tipc_udp_recv (net/tipc/udp_media.c:391) udp_rcv (net/ipv4/udp.c:2643) ip_local_deliver_finish (net/ipv4/ip_input.c:241) </IRQ> Freed by task 181: kfree (mm/slub.c:6565) bearer_disable (net/tipc/bearer.c:418) tipc_nl_bearer_disable (net/tipc/bearer.c:1001)
The bearer is freed with kfree_rcu(); free the discoverer the same way. Add an rcu_head to struct tipc_discoverer and free it and its skb from an RCU callback.
Because the RCU callback (tipc_disc_free_rcu) lives in module text, a call_rcu() that is still pending when the tipc module is unloaded would invoke a freed function. Add an rcu_barrier() to tipc_exit() after the bearer subsystem has been torn down, so all pending discoverer callbacks have run before the module text goes away.
Reachable from an unprivileged user namespace: the TIPCv2 genl family is netnsok and its bearer commands have no GENL_ADMIN_PERM. Needs CONFIG_TIPC and CONFIG_TIPC_MEDIA_UDP.
--- net/tipc/core.c | 5 +++++ net/tipc/discover.c | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-)
net/tipc/core.c +5 -0
@@ -218,6 +218,11 @@ static void __exit tipc_exit(void)unregister_pernet_device (&tipc_net_ops);tipc_unregister_sysctl (); +/* TODO: Wait for all timers that called call_rcu() to finish before +* calling rcu_barrier(). +*/ +rcu_barrier (); +pr_info ("Deactivated\n" ); }
net/tipc/discover.c +12 -2
@@ -58,6 +58,7 @@* @skb: request message to be (repeatedly) sent * @timer: timer governing period between requests * @timer_intv: current interval between requests (in ms) +* @rcu: RCU head for deferred freeing */ struct tipc_discoverer {u32 bearer_id;
@@ -69,6 +70,7 @@ struct tipc_discoverer {struct sk_buff *skb;struct timer_list timer;unsigned long timer_intv; +struct rcu_head rcu; };/**
@@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,return 0 ; } +static void tipc_disc_free_rcu (struct rcu_head *rp) +{ + struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer, + rcu); + +kfree_skb (d->skb); +kfree (d); +} +/** * tipc_disc_delete - destroy object sending periodic link setup requests * @d: ptr to link dest structure
@@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,void tipc_disc_delete (struct tipc_discoverer *d) {del_timer_sync (&d->timer); -kfree_skb (d->skb); -kfree (d); +call_rcu (&d->rcu, tipc_disc_free_rcu); }/**
Signature · 1 line
2.53.0
Re: [PATCH 5.15 231/456] tipc: fix use-after-free of the discoverer in tipc_disc_rcv()
Hi Greg/Sasha,
Quoted 111 lines · Greg Kroah-Hartman
5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weiming Shi <[email protected]>
commit
1579342d71133da7f00daa02c75cebec7372097bupstream.
bearer_disable()freesb->discwith tipc_disc_delete()'s plainkfree(), buttipc_disc_rcv()still dereferencesb->discin RX softirq underrcu_read_lock()(tipc_udp_recv->tipc_rcv->tipc_disc_rcv).L2 bearers are safe thanks to the
synchronize_net()intipc_disable_l2_media(), but the UDP bearer defers that call to thecleanup_bearer()workqueue, so the discoverer is freed with no grace period:BUG: KASAN: slab-use-after-free in tipc_disc_rcv (net/tipc/discover.c:149) Read of size 8 at addr ffff88802348b728 by task poc_tipc/184 <IRQ> tipc_disc_rcv (net/tipc/discover.c:149) tipc_rcv (net/tipc/node.c:2126) tipc_udp_recv (net/tipc/udp_media.c:391) udp_rcv (net/ipv4/udp.c:2643) ip_local_deliver_finish (net/ipv4/ip_input.c:241) </IRQ> Freed by task 181: kfree (mm/slub.c:6565) bearer_disable (net/tipc/bearer.c:418) tipc_nl_bearer_disable (net/tipc/bearer.c:1001)The bearer is freed with
kfree_rcu(); free the discoverer the same way. Add anrcu_headto structtipc_discovererand free it and its skb from an RCU callback.Because the RCU callback (
tipc_disc_free_rcu) lives in module text, acall_rcu()that is still pending when the tipc module is unloaded would invoke a freed function. Add anrcu_barrier()totipc_exit()after the bearer subsystem has been torn down, so all pending discoverer callbacks have run before the module text goes away.Reachable from an unprivileged user namespace: the TIPCv2 genl family is netnsok and its bearer commands have no
GENL_ADMIN_PERM. NeedsCONFIG_TIPCandCONFIG_TIPC_MEDIA_UDP.Fixes 25b0b9c4e835 ("tipc: handle collisions of 32-bit node address hash values")Reported-by Xiang Mei <[email protected]>Signed-off-by Weiming Shi <[email protected]>Reviewed-by Tung Nguyen <[email protected]>Signed-off-by Jakub Kicinski <[email protected]>Signed-off-by Alexander Martyniuk <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- net/tipc/core.c | 5 +++++ net/tipc/discover.c | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-)net/tipc/core.c +5
-0@@ -218,6 +218,11 @@ static void __exit tipc_exit(void)unregister_pernet_device (&tipc_net_ops);tipc_unregister_sysctl (); +/* TODO: Wait for all timers that called call_rcu() to finish before +* calling rcu_barrier(). +*/ +rcu_barrier (); +pr_info ("Deactivated\n" ); }net/tipc/discover.c +11
-0@@ -58,6 +58,7 @@* @skb: request message to be (repeatedly) sent * @timer: timer governing period between requests * @timer_intv: current interval between requests (in ms) +* @rcu: RCU head for deferred freeing */ struct tipc_discoverer {u32 bearer_id;@@ -69,6 +70,7 @@ struct tipc_discoverer {struct sk_buff *skb;struct timer_list timer;unsigned long timer_intv; +struct rcu_head rcu; };/** @@ -382,6 +384,15 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,return 0 ; } +static void tipc_disc_free_rcu (struct rcu_head *rp) +{ + struct tipc_discoverer *d = container_of(rp, struct tipc_discoverer, + rcu); + +kfree_skb (d->skb); +kfree (d); +} +/** * tipc_disc_delete - destroy object sending periodic link setup requests * @d: ptr to link dest structure
net/tipc/discover.c int tipc_disc_create(struct net *net, struct tipc_bearer *b, · line 403
Quoted 6 lines
net/tipc/discover.c +1
-2@@ -389,8 +400,7 @@ int tipc_disc_create(struct net *net, struct tipc_bearer *b,void tipc_disc_delete (struct tipc_discoverer *d) {del_timer_sync (&d->timer); -kfree_skb (d->skb); -kfree (d); +call_rcu (&d->rcu, tipc_disc_free_rcu);
^^^
I ran an AI-assisted backport review of f05b3f4c7837 and checked the finding against 5.15.y. The RCU conversion is present, but the timer lifetime part of the upstream context is missing.
Upstream commit 1579342d7113 is based on the TIPC conversion in 292a089d78d3e2f7944e60bb897c977785a321e3 and has:
timer_shutdown_sync(&d->timer);
call_rcu(&d->rcu, tipc_disc_free_rcu);The 5.15 backport instead has:
del_timer_sync(&d->timer);
call_rcu(&d->rcu, tipc_disc_free_rcu);tipc_disc_timeout() and tipc_disc_remove_dest() can call mod_timer(). Unlike timer_shutdown_sync(), del_timer_sync() does not reject a rearm after it returns. A concurrent rearm can therefore leave the timer queued while the RCU callback frees the enclosing discoverer, allowing a later timer callback to dereference freed memory.
The timer_shutdown_sync() API is already available in this 5.15 tree. Maybe we should fix with a downstream patch only ?
thanks,
harshit
Quoted 3 lines
net/tipc/discover.c +0
-0}/**
Sasha Levin
maintainerRe: [PATCH 5.15 231/456] tipc: fix use-after-free of the discoverer in tipc_disc_rcv()
Quoted 5 lines
The 5.15 backport instead has:
del_timer_sync(&d->timer); call_rcu(&d->rcu, tipc_disc_free_rcu);
Quoted 8 lines
tipc_disc_timeout()andtipc_disc_remove_dest()can callmod_timer(). Unliketimer_shutdown_sync(),del_timer_sync()does not reject a rearm after it returns. A concurrent rearm can therefore leave the timer queued while the RCU callback frees the enclosing discoverer, allowing a later timer callback to dereference freed memory.The
timer_shutdown_sync()API is already available in this 5.15 tree. Maybe we should fix with a downstream patch only ?
Good catch. Could you send the del_timer_sync() -> timer_shutdown_sync() swap in tipc_disc_delete() as a patch against 5.15.y and 6.1.y backports of this fix
- f05b3f4c7837 ("tipc: fix use-after-free of the discoverer in tipc_disc_rcv()") on 5.15.y, 4da2ac774941 ("tipc: fix use-after-free of the
discoverer in tipc_disc_rcv()") on 6.1.y?
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 232/456 wifi: mt76: mt7615: drop TXRX_NOTIFY on non-mmio buses
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Devin Wittmayer <[email protected]>
commit 39afc46c0243d10b7795e6e6cf4ae91f41732120 upstream.
PKT_TYPE_TXRX_NOTIFY is an mmio-only event, but mt7615_rx_check() and mt7615_queue_rx_skb() dispatch it to mt7615_mac_tx_free() on every bus. mt7615_mac_tx_free() cleans the DMA tx queues with mt76_queue_tx_cleanup(), which calls queue_ops->tx_cleanup(). Only the mmio queue ops implement that callback; on the mt7663 USB and SDIO buses it is NULL, so a TXRX_NOTIFY there calls a NULL pointer in the RX worker. Same defect as the mt7921 and mt7925 patches in this series.
Drop the event on non-mmio buses via mt76_is_mmio(), as in commit 5683e1488aa9 ("wifi: mt76: connac: do not check WED status for non-mmio devices").
[ No mt7615_rx_check() on this tree, so only the mt7615_queue_rx_skb()
hunk applies, and mt7615_mac_tx_free() takes the skb rather than (data, len), so the guard frees it here. ]
--- drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7615/mac.c +4 -0
@@ -1601,6 +1601,10 @@ void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,dev_kfree_skb (skb);break ;case PKT_TYPE_TXRX_NOTIFY : +if (!mt76_is_mmio (mdev)) { +dev_kfree_skb (skb); +break ; + }mt7615_mac_tx_free (dev, skb);break ;case PKT_TYPE_RX_EVENT :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 233/456 openvswitch: fix GSO userspace truncation underflow
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kyle Zeng <[email protected]>
[ Upstream commit 4032f8ed10fcb84d41c508dfb04be96589f78dfe ]
OVS_ACTION_ATTR_TRUNC currently stores a delta from the original skb length in OVS_CB(skb)->cutlen. When a later userspace action segments a GSO skb, queue_gso_packets() reuses that delta for each smaller segment. A segment can then reach queue_userspace_packet() with cutlen greater than skb->len, underflowing the length passed to skb_zerocopy().
Store the maximum preserved length instead and bound each consumer against the current skb length. Use U32_MAX as the no-truncation sentinel so the value remains valid if skb geometry changes before a consumer handles it.
--- net/openvswitch/actions.c | 15 +++++---------- net/openvswitch/datapath.c | 25 ++++++++++++++----------- net/openvswitch/datapath.h | 2 +- net/openvswitch/vport.c | 2 +- 4 files changed, 21 insertions(+), 23 deletions(-)
net/openvswitch/actions.c +5 -10
@@ -856,12 +856,8 @@ static void do_output(struct datapath *du16 mru =OVS_CB (skb)->mru;u32 cutlen =OVS_CB (skb)->cutlen; -if (unlikely (cutlen >0 )) { -if (skb->len - cutlen >ovs_mac_header_len (key)) -pskb_trim (skb, skb->len - cutlen); -else -pskb_trim (skb,ovs_mac_header_len (key)); - } +if (unlikely (cutlen < skb->len)) +pskb_trim (skb,max (cutlen,ovs_mac_header_len (key)));if (likely (!mru || (skb->len <= mru + vport->dev->hard_header_len))) {
@@ -1242,22 +1238,21 @@ static int do_execute_actions(struct dat clone =skb_clone (skb,GFP_ATOMIC );if (clone)do_output (dp, clone, port, key); -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;break ; }case OVS_ACTION_ATTR_TRUNC : {struct ovs_action_trunc *trunc =nla_data (a); -if (skb->len > trunc->max_len) -OVS_CB (skb)->cutlen = skb->len - trunc->max_len; +OVS_CB (skb)->cutlen = trunc->max_len;break ; }case OVS_ACTION_ATTR_USERSPACE :output_userspace (dp, skb, key, a, attr, len,OVS_CB (skb)->cutlen); -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;break ;case OVS_ACTION_ATTR_HASH :
net/openvswitch/datapath.c +14 -11
@@ -250,7 +250,7 @@ void ovs_dp_process_packet(struct sk_buf upcall.portid =ovs_vport_find_upcall_portid (p, skb); upcall.mru =OVS_CB (skb)->mru; - error =ovs_dp_upcall (dp, skb, key, &upcall,0 ); + error =ovs_dp_upcall (dp, skb, key, &upcall,U32_MAX );switch (error) {case 0 :case -EAGAIN :
@@ -413,7 +413,8 @@ static int queue_userspace_packet(structstruct sk_buff *nskb =NULL ;struct sk_buff *user_skb =NULL ;/* to be queued to userspace */ struct nlattr *nla; -size_t len; +size_t msg_size; +size_t skb_len;unsigned int hlen;int err, dp_ifindex;u64 hash;
@@ -434,7 +435,8 @@ static int queue_userspace_packet(struct skb = nskb; } -if (nla_attr_size (skb->len) >USHRT_MAX ) { + skb_len =min (skb->len, cutlen); +if (nla_attr_size (skb_len) >USHRT_MAX ) { err = -EFBIG ; goto out; }
@@ -449,13 +451,13 @@ static int queue_userspace_packet(struct* padding logic. Only perform zerocopy if padding is not required. */ if (dp->user_features &OVS_DP_F_UNALIGNED ) - hlen =skb_zerocopy_headlen (skb); + hlen =min (skb_zerocopy_headlen (skb), cutlen);else - hlen = skb->len; + hlen = skb_len; - len =upcall_msg_size (upcall_info, hlen - cutlen, -OVS_CB (skb)->acts_origlen); - user_skb =genlmsg_new (len,GFP_ATOMIC ); + msg_size =upcall_msg_size (upcall_info, hlen, +OVS_CB (skb)->acts_origlen); + user_skb =genlmsg_new (msg_size,GFP_ATOMIC );if (!user_skb) { err = -ENOMEM ; goto out;
@@ -516,7 +518,7 @@ static int queue_userspace_packet(struct }/* Add OVS_PACKET_ATTR_LEN when packet is truncated */ -if (cutlen >0 && +if (skb_len < skb->len &&nla_put_u32 (user_skb,OVS_PACKET_ATTR_LEN , skb->len)) { err = -ENOBUFS ; goto out;
@@ -541,9 +543,9 @@ static int queue_userspace_packet(struct err = -ENOBUFS ; goto out; } - nla->nla_len =nla_attr_size (skb->len - cutlen); + nla->nla_len =nla_attr_size (skb_len); - err =skb_zerocopy (user_skb, skb, skb->len - cutlen, hlen); + err =skb_zerocopy (user_skb, skb, skb_len, hlen);if (err) goto out;
@@ -600,6 +602,7 @@ static int ovs_packet_cmd_execute(struct packet->ignore_df =1 ; }OVS_CB (packet)->mru = mru; +OVS_CB (packet)->cutlen =U32_MAX ;if (a[OVS_PACKET_ATTR_HASH ]) { hash =nla_get_u64 (a[OVS_PACKET_ATTR_HASH ]);
net/openvswitch/datapath.h +1 -1
@@ -114,7 +114,7 @@ struct datapath {* @mru: The maximum received fragement size; 0 if the packet is not * fragmented. * @acts_origlen: The netlink size of the flow actions applied to this skb. -* @cutlen: The number of bytes from the packet end to be removed. +* @cutlen: The number of bytes in the packet to preserve on output. */ struct ovs_skb_cb {struct vport *input_vport;
net/openvswitch/vport.c +1 -1
@@ -438,7 +438,7 @@ int ovs_vport_receive(struct vport *vporOVS_CB (skb)->input_vport = vport;OVS_CB (skb)->mru =0 ; -OVS_CB (skb)->cutlen =0 ; +OVS_CB (skb)->cutlen =U32_MAX ;if (unlikely (dev_net (skb->dev) !=ovs_dp_get_net (vport->dp))) {u32 mark;
Greg Kroah-Hartman
maintainerPATCH 234/456 raw: remove unused variables from raw6_icmp_error()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit c4fceb46add65481ef0dfb79cad24c3c269b4cad upstream.
saddr and daddr are set but not used.
--- net/ipv6/raw.c | 3 --- 1 file changed, 3 deletions(-)
net/ipv6/raw.c +0 -3
@@ -335,7 +335,6 @@ static void rawv6_err(struct sock *sk, svoid raw6_icmp_error (struct sk_buff *skb,int nexthdr,u8 type,u8 code,int inner_offset,__be32 info) { -const struct in6_addr *saddr, *daddr;struct net *net =dev_net (skb->dev);struct hlist_head *head;struct sock *sk;
@@ -347,8 +346,6 @@ void raw6_icmp_error(struct sk_buff *skbsk_for_each (sk, head) {/* Note: ipv6_hdr(skb) != skb->data */ const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data; - saddr = &ip6h->saddr; - daddr = &ip6h->daddr;if (!raw_v6_match (net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr,inet6_iif (skb),inet6_sdif (skb)))
Greg Kroah-Hartman
maintainerPATCH 235/456 raw: fix a typo in raw_icmp_error()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
commit 97a4d46b1516250d640c1ae0c9e7129d160d6a1c upstream.
I accidentally broke IPv4 traceroute, by swapping iph->saddr and iph->daddr.
Probably because raw_icmp_error() and raw_v4_input() use different order for iph->saddr and iph->daddr.
--- net/ipv4/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/raw.c +1 -1
@@ -279,7 +279,7 @@ void raw_icmp_error(struct sk_buff *skb,sk_for_each (sk, head) { iph = (const struct iphdr *)skb->data;if (!raw_v4_match (net, sk, iph->protocol, - iph->saddr, iph->daddr, dif, sdif)) + iph->daddr, iph->saddr, dif, sdif))continue ;raw_err (sk, skb, info); }
Greg Kroah-Hartman
maintainerPATCH 236/456 net: mpls: initialize rtm_tos in mpls_getroute()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yehyeong Lee <[email protected]>
[ Upstream commit 295dd295e2137e10e9a5b1891d97e0f08de76f03 ]
mpls_getroute() builds the RTM_NEWROUTE reply to an RTM_GETROUTE request by filling a struct rtmsg allocated from an skb whose data area is not zeroed (alloc_skb(NLMSG_GOODSIZE, ...)). It sets every field of the header except rtm_tos:
r = nlmsg_data(nlh); r->rtm_family = AF_MPLS; r->rtm_dst_len = 20; r->rtm_src_len = 0; r->rtm_table = RT_TABLE_MAIN; r->rtm_type = RTN_UNICAST; r->rtm_scope = RT_SCOPE_UNIVERSE; r->rtm_protocol = rt->rt_protocol; r->rtm_flags = 0;
struct rtmsg has no padding, so the one uninitialised byte rtm_tos (offset 3) is copied straight to user space on recvmsg(), leaking a byte of uninitialised heap memory. This is in contrast to mpls_dump_route(), which fills the very same header and does set rtm_tos = 0.
Initialize rtm_tos to 0, matching mpls_dump_route().
Reproduced with KMSAN by adding an MPLS route and issuing a non-RTM_F_FIB_MATCH RTM_GETROUTE for its label:
BUG: KMSAN: kernel-infoleak in _copy_to_iter+0x36c/0x33f0 _copy_to_iter+0x36c/0x33f0 __skb_datagram_iter+0x196/0x12c0 skb_copy_datagram_iter+0x5b/0x210 netlink_recvmsg+0x37b/0xef0 ... Uninit was created at: __alloc_skb+0x8ca/0x10e0 mpls_getroute+0x1280/0x3a40 rtnetlink_rcv_msg+0x1138/0x15a0 ... Byte 19 of 64 is uninitialized
(byte 19 = nlmsghdr(16) + rtmsg offset 3 = rtm_tos)
--- net/mpls/af_mpls.c | 1 + 1 file changed, 1 insertion(+)
net/mpls/af_mpls.c +1 -0
@@ -2495,6 +2495,7 @@ static int mpls_getroute(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, r->rtm_family =AF_MPLS ; r->rtm_dst_len =20 ; r->rtm_src_len =0 ; + r->rtm_tos =0 ; r->rtm_table =RT_TABLE_MAIN ; r->rtm_type =RTN_UNICAST ; r->rtm_scope =RT_SCOPE_UNIVERSE ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 237/456 media: uvcvideo: Implement dual stream quirk to fix loss of usb packets
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Isaac Scott <[email protected]>
commit c2eda35e675b6ea4a0a21a4b1167b121571a9036 upstream.
Some cameras, such as the Sonix Technology Co. 292A, exhibit issues when running two parallel streams, causing USB packets to be dropped when an H.264 stream posts a keyframe while an MJPEG stream is running simultaneously. This occasionally causes the driver to erroneously output two consecutive JPEG images as a single frame.
To fix this, we inspect the buffer, and trigger a new frame when we find an SOI.
--- drivers/media/usb/uvc/uvc_video.c | 28 +++++++++++++++++++++++++++- drivers/media/usb/uvc/uvcvideo.h | 4 ++++ 2 files changed, 31 insertions(+), 1 deletion(-)
drivers/media/usb/uvc/uvc_video.c +27 -1
@@ -24,6 +24,8 @@#include "uvcvideo.h" +#define JPEG_MARKER_SOI 0xd8 +/* ------------------------------------------------------------------------ * UVC Controls */
@@ -1077,6 +1079,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,struct uvc_buffer *meta_buf, const u8 *data, int len) { + u8 header_len;u8 fid;/* Sanity checks:
@@ -1089,6 +1092,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,return -EINVAL ; } + header_len = data[0 ]; fid = data[1 ] &UVC_STREAM_FID ;/* Increase the sequence number regardless of any buffer states, so
@@ -1177,9 +1181,31 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,return -EAGAIN ; } +/* +* Some cameras, when running two parallel streams (one MJPEG alongside +* another non-MJPEG stream), are known to lose the EOF packet for a frame. +* We can detect the end of a frame by checking for a new SOI marker, as +* the SOI always lies on the packet boundary between two frames for +* these devices. +*/ +if (stream->dev->quirks &UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc ==V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc ==V4L2_PIX_FMT_JPEG )) { +const u8 *packet = data + header_len; + +if (len >= header_len +2 && + packet[0 ] ==0xff && packet[1 ] ==JPEG_MARKER_SOI && + buf->bytesused !=0 ) { + buf->state =UVC_BUF_STATE_READY ; + buf->error =1 ; + stream->last_fid ^=UVC_STREAM_FID ; +return -EAGAIN ; + } + } + stream->last_fid = fid; -return data[0 ]; +return header_len; } static inline enum dma_data_direction uvc_stream_dir(
drivers/media/usb/uvc/uvcvideo.h +4 -0
@@ -212,6 +212,10 @@#define UVC_QUIRK_FORCE_Y8 0x00000800#define UVC_QUIRK_FORCE_BPP 0x00001000#define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000 +#define UVC_QUIRK_NO_RESET_RESUME 0x00004000 +#define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000 +#define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000 +#define UVC_QUIRK_MJPEG_NO_EOF 0x00020000/* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 238/456 media: uvcvideo: Fix sequence number when no EOF
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ricardo Ribalda <[email protected]>
commit f078966ca1fb1b3865d8e6bbe2705cfd277fc637 upstream.
If the driver could not detect the EOF, the sequence number is increased twice:
1) When we enter uvc_video_decode_start() with the old buffer and FID has flipped => We return -EAGAIN and last_fid is not flipped
2) When we enter uvc_video_decode_start() with the new buffer.
Fix this issue by moving the new frame detection logic earlier in uvc_video_decode_start().
This also has some nice side affects:
- The error status from the new packet will no longer get propagated to the previous frame-buffer.
- uvc_video_clock_decode() will no longer update the previous frame buf->stf with info from the new packet.
- uvc_video_clock_decode() and uvc_video_stats_decode() will no longer get called twice for the same packet.
--- drivers/media/usb/uvc/uvc_video.c | 94 ++++++++++++++++--------------- 1 file changed, 49 insertions(+), 45 deletions(-)
drivers/media/usb/uvc/uvc_video.c +49 -45
@@ -1095,7 +1095,55 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, header_len = data[0 ]; fid = data[1 ] &UVC_STREAM_FID ; -/* Increase the sequence number regardless of any buffer states, so +/* +* Mark the buffer as done if we're at the beginning of a new frame. +* End of frame detection is better implemented by checking the EOF +* bit (FID bit toggling is delayed by one frame compared to the EOF +* bit), but some devices don't set the bit at end of frame (and the +* last payload can be lost anyway). We thus must check if the FID has +* been toggled. +* +* stream->last_fid is initialized to -1, and buf->bytesused to 0, +* so the first isochronous frame will never trigger an end of frame +* detection. +* +* Empty buffers (bytesused == 0) don't trigger end of frame detection +* as it doesn't make sense to return an empty buffer. This also +* avoids detecting end of frame conditions at FID toggling if the +* previous payload had the EOF bit set. +*/ +if (fid != stream->last_fid && buf && buf->bytesused !=0 ) { +uvc_dbg (stream->dev,FRAME , +"Frame complete (FID bit toggled)\n" ); + buf->state =UVC_BUF_STATE_READY ; + +return -EAGAIN ; + } + +/* +* Some cameras, when running two parallel streams (one MJPEG alongside +* another non-MJPEG stream), are known to lose the EOF packet for a frame. +* We can detect the end of a frame by checking for a new SOI marker, as +* the SOI always lies on the packet boundary between two frames for +* these devices. +*/ +if (stream->dev->quirks &UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc ==V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc ==V4L2_PIX_FMT_JPEG ) && + buf && buf->bytesused !=0 ) { +const u8 *packet = data + header_len; + +if (len >= header_len +2 && + packet[0 ] ==0xff && packet[1 ] ==JPEG_MARKER_SOI ) { + buf->state =UVC_BUF_STATE_READY ; + buf->error =1 ; + stream->last_fid ^=UVC_STREAM_FID ; +return -EAGAIN ; + } + } + +/* +* Increase the sequence number regardless of any buffer states, so * that discontinuous sequence numbers always indicate lost frames. */ if (stream->last_fid != fid) {
@@ -1159,50 +1207,6 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, meta_buf->state =UVC_BUF_STATE_ACTIVE ; } -/* Mark the buffer as done if we're at the beginning of a new frame. -* End of frame detection is better implemented by checking the EOF -* bit (FID bit toggling is delayed by one frame compared to the EOF -* bit), but some devices don't set the bit at end of frame (and the -* last payload can be lost anyway). We thus must check if the FID has -* been toggled. -* -* stream->last_fid is initialized to -1, so the first isochronous -* frame will never trigger an end of frame detection. -* -* Empty buffers (bytesused == 0) don't trigger end of frame detection -* as it doesn't make sense to return an empty buffer. This also -* avoids detecting end of frame conditions at FID toggling if the -* previous payload had the EOF bit set. -*/ -if (fid != stream->last_fid && buf->bytesused !=0 ) { -uvc_dbg (stream->dev,FRAME , -"Frame complete (FID bit toggled)\n" ); - buf->state =UVC_BUF_STATE_READY ; -return -EAGAIN ; - } - -/* -* Some cameras, when running two parallel streams (one MJPEG alongside -* another non-MJPEG stream), are known to lose the EOF packet for a frame. -* We can detect the end of a frame by checking for a new SOI marker, as -* the SOI always lies on the packet boundary between two frames for -* these devices. -*/ -if (stream->dev->quirks &UVC_QUIRK_MJPEG_NO_EOF && - (stream->cur_format->fcc ==V4L2_PIX_FMT_MJPEG || - stream->cur_format->fcc ==V4L2_PIX_FMT_JPEG )) { -const u8 *packet = data + header_len; - -if (len >= header_len +2 && - packet[0 ] ==0xff && packet[1 ] ==JPEG_MARKER_SOI && - buf->bytesused !=0 ) { - buf->state =UVC_BUF_STATE_READY ; - buf->error =1 ; - stream->last_fid ^=UVC_STREAM_FID ; -return -EAGAIN ; - } - } - stream->last_fid = fid;return header_len;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 239/456 gve: fix Rx queue stall on alloc failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eddie Phillips <[email protected]>
commit b65352a1bac64442ad95e64f385b40ccb9f1b0db upstream.
When the system is under extreme memory pressure, page allocations can fail during the Rx buffer refill loop. If the number of buffers posted to hardware falls below a critical low threshold and the refill loop exits due to allocation failures, the queue can stall:
1. The device drops incoming packets because there are no descriptors.
2. Since no packets are processed, no Rx completions are generated.
3. Because no completions occur, NAPI is never scheduled, preventing the refill loop from running again even after memory is freed.
This results in a permanent queue stall.
Resolve this by introducing a starvation recovery timer for each Rx queue. If the number of buffers posted to hardware falls below a critical low threshold, start a timer to periodically reschedule NAPI. Once NAPI runs and successfully refills the queue above the threshold, the timer is not rescheduled.
The threshold is set to 32 because a single maximum-sized Receive Segment Coalescing (RSC) packet can consume up to 19 descriptors in the Rx path. Lower thresholds (such as 8 or 16) would be insufficient to process a complete maximum-sized RSC packet, risking packet drops or unexpected hardware behavior under memory pressure. Setting the threshold to 32 guarantees a safe margin to handle at least one full RSC packet.
--- drivers/net/ethernet/google/gve/gve.h | 3 ++ drivers/net/ethernet/google/gve/gve_main.c | 3 ++ drivers/net/ethernet/google/gve/gve_rx_dqo.c | 33 ++++++++++++++++++++ 3 files changed, 39 insertions(+)
drivers/net/ethernet/google/gve/gve.h +3 -0
@@ -10,6 +10,7 @@#include <linux/dma-mapping.h> #include <linux/netdevice.h> #include <linux/pci.h> +#include <linux/timer.h> #include <linux/u64_stats_sync.h> #include "gve_desc.h"
@@ -35,6 +36,7 @@/* Interval to schedule a stats report update, 20000ms. */ #define GVE_STATS_REPORT_TIMER_PERIOD 20000 +#define GVE_RX_NAPI_RESCHED_MS 20/* msecs */ /* Numbers of NIC tx/rx stats in stats report. */ #define NIC_TX_STATS_REPORT_NUM 0
@@ -218,6 +220,7 @@ struct gve_rx_ring {struct u64_stats_sync statss;/* sync stats for 32bit archs */ struct gve_rx_ctx ctx;/* Info for packet currently being processed in this ring. */ +struct timer_list starvation_timer;/* for queue starvation recovery */ };/* A TX desc ring entry */
drivers/net/ethernet/google/gve/gve_main.c +3 -0
@@ -508,6 +508,9 @@ static void gve_remove_napi(struct gve_priv *priv, int ntfy_idx) {struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; +if (block->rx && !gve_is_gqi (priv)) +timer_shutdown_sync (&block->rx->starvation_timer); +netif_napi_del (&block->napi); }
drivers/net/ethernet/google/gve/gve_rx_dqo.c +33 -0
@@ -16,6 +16,16 @@#include <net/ipv6.h> #include <net/tcp.h> +static void gve_rx_starvation_timer (struct timer_list *t) +{ +struct gve_rx_ring *rx =from_timer (rx, t, starvation_timer); +struct gve_priv *priv = rx->gve; +struct gve_notify_block *block; + + block = &priv->ntfy_blocks[rx->ntfy_id]; +napi_schedule (&block->napi); +} +static int gve_buf_ref_cnt (struct gve_rx_buf_state_dqo *bs) {return page_count (bs->page_info.page) - bs->page_info.pagecnt_bias;
@@ -185,6 +195,7 @@ static void gve_rx_free_ring_dqo(struct gve_priv *priv, int idx) completion_queue_slots = rx->dqo.complq.mask +1 ; buffer_queue_slots = rx->dqo.bufq.mask +1 ; +timer_shutdown_sync (&rx->starvation_timer);gve_rx_remove_from_block (priv, idx);if (rx->q_resources) {
@@ -237,6 +248,7 @@ static int gve_rx_alloc_ring_dqo(struct gve_priv *priv, int idx)memset (rx,0 ,sizeof (*rx)); rx->gve = priv; rx->q_num = idx; +timer_setup (&rx->starvation_timer, gve_rx_starvation_timer,0 ); rx->dqo.bufq.mask = buffer_queue_slots -1 ; rx->dqo.complq.num_free_slots = completion_queue_slots; rx->dqo.complq.mask = completion_queue_slots -1 ;
@@ -334,6 +346,7 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq;struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq;struct gve_priv *priv = rx->gve; +u32 num_bufs_avail_to_hw;u32 num_avail_slots;u32 num_full_slots;u32 num_posted =0 ;
@@ -374,6 +387,26 @@ void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) } rx->fill_cnt += num_posted; + +/* If the queue has fewer than GVE_RX_BUF_THRESH_DQO descriptors +* visible to the hardware, the hardware is in danger of starving +* and cannot trigger interrupts. +* +* We use a threshold of 32 because a single maximum-sized RSC +* packet can consume up to 19 descriptors in the Rx path. Lower +* thresholds (e.g., 8 or 16) would be unsafe as they could cause +* the device to drop/stall on a maximum-sized RSC packet. +* +* Start the timer to periodically reschedule NAPI and recover. +*/ + num_bufs_avail_to_hw = + ((bufq->tail & ~(GVE_RX_BUF_THRESH_DQO -1 )) - + bufq->head) & bufq->mask; + +if (num_bufs_avail_to_hw <GVE_RX_BUF_THRESH_DQO ) { +mod_timer (&rx->starvation_timer, + jiffies +msecs_to_jiffies (GVE_RX_NAPI_RESCHED_MS )); + } } static void gve_try_recycle_buf(struct gve_priv *priv, struct gve_rx_ring *rx,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 240/456 HID: logitech-dj: Standardise hid_report_enum variable nomenclature
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <[email protected]>
[ Upstream commit a940aee176437046598dfc786b719bd96db3c74c ]
Since we will need to differentiate between the two report_enum types soon, let's unify the naming conventions now to save confusion and/or unnecessary/unrelated changes in upcoming commits.
{input,output}_report_enum is used in other places to let's conform.
--- drivers/hid/hid-logitech-dj.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/hid/hid-logitech-dj.c +6 -6
@@ -1749,7 +1749,7 @@ static int logi_dj_raw_event(struct hid_device *hdev,static int logi_dj_probe (struct hid_device *hdev,const struct hid_device_id *id) { -struct hid_report_enum *rep_enum; +struct hid_report_enum *input_report_enum;struct hid_report *rep;struct dj_receiver_dev *djrcv_dev;struct usb_interface *intf;
@@ -1793,10 +1793,10 @@ static int logi_dj_probe(struct hid_device *hdev, } } - rep_enum = &hdev->report_enum[HID_INPUT_REPORT ]; + input_report_enum = &hdev->report_enum[HID_INPUT_REPORT ];/* no input reports, bail out */ -if (list_empty (&rep_enum->report_list)) +if (list_empty (&input_report_enum->report_list))return -ENODEV ;/*
@@ -1804,7 +1804,7 @@ static int logi_dj_probe(struct hid_device *hdev,* Note: we should theoretically check for HID++ and DJ * collections, but this will do. */ -list_for_each_entry (rep, &rep_enum->report_list, list) { +list_for_each_entry (rep, &input_report_enum->report_list, list) {if (rep->application ==0xff000001 ) has_hidpp = true; }
@@ -1817,7 +1817,7 @@ static int logi_dj_probe(struct hid_device *hdev,return -ENODEV ;/* get the current application attached to the node */ - rep = list_first_entry(&rep_enum->report_list, struct hid_report, list); + rep = list_first_entry(&input_report_enum->report_list, struct hid_report, list); djrcv_dev =dj_get_receiver_dev (hdev, id->driver_data, rep->application, has_hidpp);if (!djrcv_dev) {
@@ -1825,7 +1825,7 @@ static int logi_dj_probe(struct hid_device *hdev,return -ENOMEM ; } -if (!rep_enum->numbered) +if (!input_report_enum->numbered) djrcv_dev->unnumbered_application = rep->application;/* Starts the usb device and connects to upper interfaces hiddev and
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 241/456 HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jones <[email protected]>
[ Upstream commit b6a57912854e7ea36f3b270032661140cc4209cd ]
logi_dj_recv_send_report() assumes that all incoming REPORT_ID_DJ_SHORT reports are 14 Bytes (DJREPORT_SHORT_LENGTH - 1) long. It uses that assumption to load the associated field's 'value' array with 14 Bytes of data. However, if a malicious user only sends say 1 Byte of data, 'report_count' will be 1 and only 1 Byte of memory will be allocated to the 'value' Byte array. When we come to populate 'value[1-13]' we will experience an OOB write.
--- drivers/hid/hid-logitech-dj.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/hid/hid-logitech-dj.c +10 -0
@@ -1750,6 +1750,7 @@ static int logi_dj_probe(struct hid_device *hdev, const struct hid_device_id *id) {struct hid_report_enum *input_report_enum; +struct hid_report_enum *output_report_enum;struct hid_report *rep;struct dj_receiver_dev *djrcv_dev;struct usb_interface *intf;
@@ -1793,6 +1794,15 @@ static int logi_dj_probe(struct hid_device *hdev, } } + output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT ]; + rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT ]; + +if (rep->maxfield <1 || 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 ; + } + input_report_enum = &hdev->report_enum[HID_INPUT_REPORT ];/* no input reports, bail out */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 242/456 HID: logitech-dj: fix wrong detection of bad DJ_SHORT output report
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Tissoires <[email protected]>
[ Upstream commit 8b9a097eb2fc37b486afd81388c693bf3ab44466 ]
commit b6a57912854e ("HID: logitech-dj: Prevent REPORT_ID_DJ_SHORT related user initiated OOB write") assumed that all HID devices attached to the logitech-dj driver was having an output report of DJ_SHORT.
However, on the receiver itself, we have 2 other HID device we attach
here: the mouse emulation and the keyboard emulation. For those devices the value of rep is NULL and we are triggered a segfault here.
This is doubly required because logitech-dj also handles non DJ devices that might not have the DJ collection.
--- drivers/hid/hid-logitech-dj.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/hid/hid-logitech-dj.c +2 -1
@@ -1797,7 +1797,8 @@ static int logi_dj_probe(struct hid_device *hdev, output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT ]; rep = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT ]; -if (rep->maxfield <1 || rep->field[0 ]->report_count !=DJREPORT_SHORT_LENGTH -1 ) { +if (rep && (rep->maxfield <1 || + 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 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 243/456 net: qrtr: ns: Limit the maximum server registration per node
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Manivannan Sadhasivam <[email protected]>
[ Upstream commit d5ee2ff98322337951c56398e79d51815acbf955 ]
Current code does no bound checking on the number of servers added per node. A malicious client can flood NEW_SERVER messages and exhaust memory.
Fix this issue by limiting the maximum number of server registrations to 256 per node. If the NEW_SERVER message is received for an old port, then don't restrict it as it will get replaced. While at it, also rate limit the error messages in the failure path of qrtr_ns_worker().
Note that the limit of 256 is chosen based on the current platform requirements. If requirement changes in the future, this limit can be increased.
--- net/qrtr/ns.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-)
net/qrtr/ns.c +16 -10
@@ -68,13 +68,9 @@ struct qrtr_server {struct qrtr_node {unsigned int id;struct xarray servers; +u32 server_count; }; -/* Max lookup limit is chosen based on the current platform requirements. If the -* requirement changes in the future, this value can be increased. -*/ -#define QRTR_NS_MAX_LOOKUPS 64 -/* Max nodes, server, lookup limits are chosen based on the current platform * requirements. If the requirement changes in the future, these values can be * increased.
@@ -249,6 +245,17 @@ static struct qrtr_server *server_add(unsigned int service,if (!service || !port)return NULL ; + node =node_get (node_id); +if (!node) +return NULL ; + +/* Make sure the new servers per port are capped at the maximum value */ + old =xa_load (&node->servers, port); +if (!old && node->server_count >=QRTR_NS_MAX_SERVERS ) { +pr_err_ratelimited ("QRTR client node %u exceeds max server limit!\n" , node_id); +return NULL ; + } + srv =kzalloc (sizeof (*srv),GFP_KERNEL );if (!srv)return NULL ;
@@ -258,10 +265,6 @@ static struct qrtr_server *server_add(unsigned int service, srv->node = node_id; srv->port = port; - node =node_get (node_id); -if (!node) - goto err; -/* Delete the old server on the same port */ old =xa_store (&node->servers, port, srv,GFP_KERNEL );if (old) {
@@ -272,6 +275,8 @@ static struct qrtr_server *server_add(unsigned int service, }else {kfree (old); } + }else { + node->server_count++; } trace_qrtr_ns_server_add(srv->service, srv->instance,
@@ -312,6 +317,7 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast) }kfree (srv); + node->server_count--;return 0 ; }
@@ -708,7 +714,7 @@ static void qrtr_ns_worker(struct work_struct *work) }if (ret <0 ) -pr_err ("failed while handling packet from %d:%d" , +pr_err_ratelimited ("failed while handling packet from %d:%d" , sq.sq_node, sq.sq_port); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 244/456 net: qrtr: ns: Raise node count limit to 512
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Youssef Samir <[email protected]>
[ Upstream commit ff194cffd586cbd4cc49eccb002c65f2a902a277 ]
The current node limit of 64 breaks the functionality for a number of AI200 deployments that have up to 384 nodes. Raise the limit to 512.
Also, the backport of commit 27d5e84e810b ("net: qrtr: ns: Limit the total number of nodes") to 5.10, 5.15 and 6.1 dropped the node_count-- hunk in ctrl_cmd_bye(). Add it back.
--- net/qrtr/ns.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
net/qrtr/ns.c +3 -2
@@ -75,11 +75,11 @@ struct qrtr_node {* requirements. If the requirement changes in the future, these values can be * increased. */ -#define QRTR_NS_MAX_NODES 64 +#define QRTR_NS_MAX_NODES 512#define QRTR_NS_MAX_SERVERS 256#define QRTR_NS_MAX_LOOKUPS 64 -static u8 node_count; +static u16 node_count;static struct qrtr_node *node_get (unsigned int node_id) {
@@ -412,6 +412,7 @@ static int ctrl_cmd_bye(struct sockaddr_qrtr *from) delete_node:xa_erase (&nodes, from->sq_node);kfree (node); + node_count--;return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 245/456 tls: separate no-async decryption request handling from async
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sabrina Dubroca <[email protected]>
commit 41532b785e9d79636b3815a64ddf6a096647d011 upstream.
If we're not doing async, the handling is much simpler. There's no reference counting, we just need to wait for the completion to wake us up and return its result.
We should preferably also use a separate crypto_wait. I'm not seeing a UAF as I did in the past, I think aec7961916f3 ("tls: fix race between async notify and socket close") took care of it.
This will make the next fix easier.
--- net/tls/tls_sw.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
net/tls/tls_sw.c +8 -5
@@ -278,9 +278,15 @@ static int tls_do_decryption(struct sock *sk,BUILD_BUG_ON_INVALID (atomic_read (&ctx->decrypt_pending) <1 );atomic_inc (&ctx->decrypt_pending); }else { +DECLARE_CRYPTO_WAIT (wait); +aead_request_set_callback (aead_req,CRYPTO_TFM_REQ_MAY_BACKLOG , - crypto_req_done, &ctx->async_wait); + crypto_req_done, &wait); + ret =crypto_aead_decrypt (aead_req); +if (ret == -EINPROGRESS || ret == -EBUSY ) + ret =crypto_wait_req (ret, &wait); +return ret; } ret =crypto_aead_decrypt (aead_req);
@@ -289,10 +295,7 @@ static int tls_do_decryption(struct sock *sk, ret = ret ?: -EINPROGRESS ; }if (ret == -EINPROGRESS ) { -if (darg->async) -return 0 ; - - ret =crypto_wait_req (ret, &ctx->async_wait); +return 0 ; }else if (darg->async) {atomic_dec (&ctx->decrypt_pending); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 246/456 dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongling Zeng <[email protected]>
[ Upstream commit ab1150115e68a46b687eb38c1ab92782018c9f2c ]
When terminating DMA transfers, active descriptors are not properly reclaimed. Only cyclic descriptors were handled, leaving non-cyclic descriptors and their LLI chains to be permanently leaked.
Fix by using vchan_terminate_vdesc() which handles both cyclic and non-cyclic descriptors by adding them to desc_terminated queue for proper cleanup.
Add pchan->desc != pchan->done check to prevent double-adding completed descriptors, which would corrupt the list.
--- drivers/dma/sun6i-dma.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
drivers/dma/sun6i-dma.c +4 -7
@@ -895,16 +895,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)spin_lock_irqsave (&vchan->vc.lock, flags); -if (vchan->cyclic) { - vchan->cyclic = false; -if (pchan && pchan->desc) { -struct virt_dma_desc *vd = &pchan->desc->vd; -struct virt_dma_chan *vc = &vchan->vc; +if (pchan && pchan->desc && pchan->desc != pchan->done) { +struct virt_dma_desc *vd = &pchan->desc->vd; -list_add_tail (&vd->node, &vc->desc_completed); - } +vchan_terminate_vdesc (vd); } + vchan->cyclic = false;vchan_get_all_descriptors (&vchan->vc, &head);if (pchan) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 247/456 ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Uday Khare <[email protected]>
[ Upstream commit 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 ]
In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard.
The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check:
if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98095->mclk holding an error pointer with no indication to the caller.
This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field.
Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers.
--- sound/soc/codecs/max98095.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
sound/soc/codecs/max98095.c +3 -2
@@ -1990,8 +1990,9 @@ static int max98095_probe(struct snd_soc_component *component)int ret =0 ; max98095->mclk =devm_clk_get (component->dev,"mclk" ); -if (PTR_ERR (max98095->mclk) == -EPROBE_DEFER ) -return -EPROBE_DEFER ; +if (IS_ERR (max98095->mclk)) +if (PTR_ERR (max98095->mclk) == -EPROBE_DEFER ) +return -EPROBE_DEFER ;/* reset the codec, the DSP core, and disable all interrupts */ max98095_reset (component);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 248/456 ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Uday Khare <[email protected]>
[ Upstream commit a792ce0fad61a70793ec565743f11d6ca534de59 ]
In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard.
The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check:
if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98090->mclk holding an error pointer with no indication to the caller.
This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field.
Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers.
--- sound/soc/codecs/max98090.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
sound/soc/codecs/max98090.c +3 -2
@@ -2391,8 +2391,9 @@ static int max98090_probe(struct snd_soc_component *component)dev_dbg (component->dev,"max98090_probe\n" ); max98090->mclk =devm_clk_get (component->dev,"mclk" ); -if (PTR_ERR (max98090->mclk) == -EPROBE_DEFER ) -return -EPROBE_DEFER ; +if (IS_ERR (max98090->mclk)) +if (PTR_ERR (max98090->mclk) == -EPROBE_DEFER ) +return -EPROBE_DEFER ; max98090->component = component;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 249/456 phy: zynqmp: Allow variation in refclk rate
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Anderson <[email protected]>
[ Upstream commit 76009ee76e05e30e29aade02e788aebe9ce9ffd2 ]
Due to limited available frequency ratios, the reference clock rate may not be exactly the same as the required rate. Allow a small (100 ppm) deviation.
Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
--- drivers/phy/xilinx/phy-zynqmp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/phy/xilinx/phy-zynqmp.c +4 -1
@@ -957,7 +957,10 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) rate =clk_get_rate (clk);for (i =0 ; i <ARRAY_SIZE (ssc_lookup); i++) { -if (rate == ssc_lookup[i].refclk_rate) { +/* Allow an error of 100 ppm */ +unsigned long error = ssc_lookup[i].refclk_rate /10000 ; + +if (abs (rate - ssc_lookup[i].refclk_rate) < error) { gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i];break ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 250/456 phy-zynqmp: Postpone getting clock rate until actually needed
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mike Looijmans <[email protected]>
[ Upstream commit 065d5885f6180c534b7b176847b3e008f4e11850 ]
At probe time the driver would display the following error and abort:
xilinx-psgtr fd400000.phy: Invalid rate 0 for reference clock 0
At probe time, the associated GTR driver (e.g. SATA or PCIe) hasn't initialized the clock yet, so clk_get_rate() likely returns 0 if the clock is programmable. So this driver only works if the clock is fixed.
The PHY driver doesn't need to know the clock frequency at probe yet, so wait until the associated driver initializes the lane before requesting the clock rate setting.
In addition to allowing the driver to be used with programmable clocks, this also reduces the driver's runtime memory footprint by removing an array of pointers from struct xpsgtr_phy.
Stable-dep-of: e4779e2a16d6 ("phy: zynqmp: fix clock error handling in xpsgtr_phy_init()")
--- drivers/phy/xilinx/phy-zynqmp.c | 70 +++++++++++++++++---------------- 1 file changed, 37 insertions(+), 33 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +37 -33
@@ -228,7 +228,6 @@ struct xpsgtr_phy {* @siou: siou base address * @gtr_mutex: mutex for locking * @phys: PHY lanes -* @refclk_sscs: spread spectrum settings for the reference clocks * @clk: reference clocks * @tx_term_fix: fix for GT issue * @saved_icm_cfg0: stored value of ICM CFG0 register
@@ -241,7 +240,6 @@ struct xpsgtr_dev {void __iomem *siou;struct mutex gtr_mutex;/* mutex for locking */ struct xpsgtr_phy phys[NUM_LANES ]; -const struct xpsgtr_ssc *refclk_sscs[NUM_LANES ];struct clk *clk[NUM_LANES ];bool tx_term_fix;unsigned int saved_icm_cfg0;
@@ -384,13 +382,40 @@ static int xpsgtr_wait_pll_lock(struct phy *phy)return ret; } +/* Get the spread spectrum (SSC) settings for the reference clock rate */ +static const struct xpsgtr_ssc *xpsgtr_find_sscs (struct xpsgtr_phy *gtr_phy) +{ +unsigned long rate; +struct clk *clk; +unsigned int i; + + clk = gtr_phy->dev->clk[gtr_phy->refclk]; + rate =clk_get_rate (clk); + +for (i =0 ; i <ARRAY_SIZE (ssc_lookup); i++) { +/* Allow an error of 100 ppm */ +unsigned long error = ssc_lookup[i].refclk_rate /10000 ; + +if (abs (rate - ssc_lookup[i].refclk_rate) < error) +return &ssc_lookup[i]; + } + +dev_err (gtr_phy->dev->dev,"Invalid rate %lu for reference clock %u\n" , + rate, gtr_phy->refclk); + +return NULL ; +} +/* Configure PLL and spread-sprectrum clock. */ -static void xpsgtr_configure_pll (struct xpsgtr_phy *gtr_phy) +static int xpsgtr_configure_pll (struct xpsgtr_phy *gtr_phy) {const struct xpsgtr_ssc *ssc;u32 step_size; - ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; + ssc =xpsgtr_find_sscs (gtr_phy); +if (!ssc) +return -EINVAL ; + step_size = ssc->step_size; xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane),
@@ -432,6 +457,8 @@ static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy)xpsgtr_clr_set_phy (gtr_phy,L0_PLL_SS_STEP_SIZE_3_MSB ,STEP_SIZE_3_MASK , (step_size &STEP_SIZE_3_MASK ) |FORCE_STEP_SIZE |FORCE_STEPS ); + +return 0 ; }/* Configure the lane protocol. */
@@ -644,7 +671,10 @@ static int xpsgtr_phy_init(struct phy *phy)* Configure the PLL, the lane protocol, and perform protocol-specific * initialization. */ -xpsgtr_configure_pll (gtr_phy); + ret =xpsgtr_configure_pll (gtr_phy); +if (ret) + goto out; +xpsgtr_lane_set_protocol (gtr_phy);switch (gtr_phy->protocol) {
@@ -854,8 +884,7 @@ static struct phy *xpsgtr_xlate(struct device *dev, } refclk = args->args[3 ]; -if (refclk >=ARRAY_SIZE (gtr_dev->refclk_sscs) || - !gtr_dev->refclk_sscs[refclk]) { +if (refclk >=ARRAY_SIZE (gtr_dev->clk)) {dev_err (dev,"Invalid reference clock number %u\n" , refclk);return ERR_PTR (-EINVAL ); }
@@ -931,9 +960,7 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) {unsigned int refclk; -for (refclk =0 ; refclk <ARRAY_SIZE (gtr_dev->refclk_sscs); ++refclk) { -unsigned long rate; -unsigned int i; +for (refclk =0 ; refclk <ARRAY_SIZE (gtr_dev->clk); ++refclk) {struct clk *clk;char name[8 ];
@@ -949,29 +976,6 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev)continue ; gtr_dev->clk[refclk] = clk; - -/* -* Get the spread spectrum (SSC) settings for the reference -* clock rate. -*/ - rate =clk_get_rate (clk); - -for (i =0 ; i <ARRAY_SIZE (ssc_lookup); i++) { -/* Allow an error of 100 ppm */ -unsigned long error = ssc_lookup[i].refclk_rate /10000 ; - -if (abs (rate - ssc_lookup[i].refclk_rate) < error) { - gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; -break ; - } - } - -if (i ==ARRAY_SIZE (ssc_lookup)) { -dev_err (gtr_dev->dev, -"Invalid rate %lu for reference clock %u\n" , - rate, refclk); -return -EINVAL ; - } }return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 251/456 phy: zynqmp: fix clock error handling in xpsgtr_phy_init()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radhey Shyam Pandey <[email protected]>
[ Upstream commit e4779e2a16d600892aaf743438f6ce8cc4eb3c4c ]
Propagate clk_prepare_enable() failures to the caller instead of returning success, and disable the reference clock on initialization error paths to avoid leaking clock references when phy_exit() is not called.
--- drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +9 -4
@@ -644,12 +644,13 @@ static int xpsgtr_phy_init(struct phy *phy) {struct xpsgtr_phy *gtr_phy =phy_get_drvdata (phy);struct xpsgtr_dev *gtr_dev = gtr_phy->dev; -int ret =0 ; +int ret;mutex_lock (>r_dev->gtr_mutex);/* Configure and enable the clock when peripheral phy_init call */ -if (clk_prepare_enable (gtr_dev->clk[gtr_phy->refclk])) + ret =clk_prepare_enable (gtr_dev->clk[gtr_phy->refclk]); +if (ret) goto out;/* Skip initialization if not required. */
@@ -659,7 +660,7 @@ static int xpsgtr_phy_init(struct phy *phy)if (gtr_dev->tx_term_fix) { ret =xpsgtr_phy_tx_term_fix (gtr_phy);if (ret <0 ) - goto out; + goto out_disable_clk; gtr_dev->tx_term_fix = false; }
@@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy)*/ ret =xpsgtr_configure_pll (gtr_phy);if (ret) - goto out; + goto out_disable_clk;xpsgtr_lane_set_protocol (gtr_phy);
@@ -691,6 +692,10 @@ static int xpsgtr_phy_init(struct phy *phy)break ; } + goto out; + +out_disable_clk: +clk_disable_unprepare (gtr_dev->clk[gtr_phy->refclk]); out:mutex_unlock (>r_dev->gtr_mutex);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 252/456 phy: zynqmp: fix runtime PM leak on probe allocation failure
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Radhey Shyam Pandey <[email protected]>
[ Upstream commit f3506e15cf72e94f62d5f2d173e5b7008f644cde ]
Allocate saved_regs before pm_runtime_resume_and_get() so a devm_kmalloc() failure does not leave an unreleased runtime PM usage counter.
--- drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/phy/xilinx/phy-zynqmp.c +6 -6
@@ -1045,6 +1045,12 @@ static int xpsgtr_probe(struct platform_device *pdev)return PTR_ERR (provider); } + gtr_dev->saved_regs =devm_kmalloc (gtr_dev->dev, +sizeof (save_reg_address), +GFP_KERNEL ); +if (!gtr_dev->saved_regs) +return -ENOMEM ; +pm_runtime_set_active (gtr_dev->dev);pm_runtime_enable (gtr_dev->dev);
@@ -1054,12 +1060,6 @@ static int xpsgtr_probe(struct platform_device *pdev)return ret; } - gtr_dev->saved_regs =devm_kmalloc (gtr_dev->dev, -sizeof (save_reg_address), -GFP_KERNEL ); -if (!gtr_dev->saved_regs) -return -ENOMEM ; -return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 253/456 netfilter: nf_conntrack_sip: widen NAT rewrite delta to s32 in sip_help_tcp()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit db3d0e0e5d4bc5ab4fe445b9f413d1b486508ca5 ]
sip_help_tcp() stores the size change of each NAT-rewritten SIP message in s16 diff and accumulates it in s16 tdiff, but a single message can grow by more than S16_MAX while the packet stays under the 65535 enlarge_skb() limit: nf_nat_sip() rewrites every matching URI, and a long Contact list expands the message by tens of kilobytes. diff then wraps, and "datalen = datalen + diff - msglen" yields a huge unsigned datalen, so the next iteration's ct_sip_get_header() reads past the linearized skb tail.
Widen diff, tdiff and the seq_adjust hook to s32. Both are bounded by the 65535 byte packet limit, and the seqadj core is already s32 (nf_ct_seqadj_set() takes s32), so no previously accepted input is rejected.
BUG: KASAN: use-after-free in ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) Read of size 1 at addr ffff888010800000 by task ksoftirqd/1/25 ct_sip_get_header (net/netfilter/nf_conntrack_sip.c:464) sip_help_tcp (net/netfilter/nf_conntrack_sip.c:1694) nf_confirm (net/netfilter/nf_conntrack_proto.c:183) nf_hook_slow (net/netfilter/core.c:619) ip6_output (net/ipv6/ip6_output.c:246) ip6_forward (net/ipv6/ip6_output.c:690) ipv6_rcv (net/ipv6/ip6_input.c:351) __netif_receive_skb_one_core (net/core/dev.c:6212) process_backlog (net/core/dev.c:6676) __napi_poll (net/core/dev.c:7735) net_rx_action (net/core/dev.c:7955) handle_softirqs (kernel/softirq.c:622) run_ksoftirqd (kernel/softirq.c:1076) ...
--- include/linux/netfilter/nf_conntrack_sip.h | 2 +- net/netfilter/nf_conntrack_sip.c | 2 +- net/netfilter/nf_nat_sip.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
include/linux/netfilter/nf_conntrack_sip.h +1 -1
@@ -115,7 +115,7 @@ struct nf_nat_sip_hooks { unsigned int *datalen);void (*seq_adjust)(struct sk_buff *skb, -unsigned int protoff,s16 off); +unsigned int protoff,s32 off); unsigned int (*expect)(struct sk_buff *skb, unsigned int protoff,
net/netfilter/nf_conntrack_sip.c +1 -1
@@ -1622,7 +1622,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,unsigned int matchoff, matchlen;unsigned int msglen, origlen;const char *dptr, *end; -s16 diff, tdiff =0 ; +s32 diff, tdiff =0 ;int ret =NF_ACCEPT ;unsigned long clen;bool term;
net/netfilter/nf_nat_sip.c +1 -1
@@ -313,7 +313,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, }static void nf_nat_sip_seq_adjust (struct sk_buff *skb,unsigned int protoff, -s16 off) +s32 off) {enum ip_conntrack_info ctinfo;struct nf_conn *ct =nf_ct_get (skb, &ctinfo);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 254/456 drm/mediatek: Check CRTC state before freeing
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ruoyu Wang <[email protected]>
[ Upstream commit 233a4d3a39fc1585f5e271b2adab43c6af025ae0 ]
mtk_crtc_reset() destroys the current CRTC state only when crtc->state is non-NULL, but it always converts crtc->state to struct mtk_crtc_state and passes the result to kfree().
When reset is called without an existing state, container_of(NULL, ...) does not produce NULL. Keep the mtk state free in the same crtc->state guard as the helper state destruction.
This issue was found by a static analysis checker and confirmed by manual source review.
--- drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/mediatek/mtk_drm_crtc.c +3 -3
@@ -172,10 +172,10 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc) {struct mtk_crtc_state *state; -if (crtc->state) +if (crtc->state) {__drm_atomic_helper_crtc_destroy_state (crtc->state); - -kfree (to_mtk_crtc_state (crtc->state)); +kfree (to_mtk_crtc_state (crtc->state)); + } crtc->state =NULL ; state =kzalloc (sizeof (*state),GFP_KERNEL );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 255/456 keys: fix out-of-bounds read in keyring_get_key_chunk()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit 63918731f9ae25b5deb022f118e941e6dddfcef4 ]
For description-level chunks keyring_get_key_chunk() advances the read pointer by level * sizeof(long) past the inline prefix but only bounds-checks the prefix, so a long enough key description is read past its kmemdup(desc, desc_len + 1) allocation. Compute the full byte offset and bounds-check the description against it before reading.
The walk only reaches a description-level chunk when two keys collide through the hash, x, type and domain_tag chunks, so this is reached from an unprivileged add_key(2) with a crafted pair of same-type keys whose index hashes collide; KASAN reports a slab-out-of-bounds read.
--- security/keys/keyring.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
security/keys/keyring.c +5 -4
@@ -271,6 +271,7 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)unsigned long chunk =0 ;const u8 *d;int desc_len = index_key->desc_len, n =sizeof (chunk); +unsigned int offset; level /=ASSOC_ARRAY_KEY_CHUNK_SIZE ;switch (level) {
@@ -284,12 +285,12 @@ static unsigned long keyring_get_key_chunk(const void *data, int level)return (unsigned long )index_key->domain_tag;default : level -=4 ; -if (desc_len <=sizeof (index_key->desc)) + offset =sizeof (index_key->desc) + level *sizeof (long ); +if (desc_len <= offset)return 0 ; - d = index_key->description +sizeof (index_key->desc); - d += level *sizeof (long ); - desc_len -=sizeof (index_key->desc); + d = index_key->description + offset; + desc_len -= offset;if (desc_len > n) desc_len = n; do {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 256/456 keys: make keyring key-chunk byte order agree with keyring_diff_objects()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit 58565eef0f8d861aae92abfb7658458d661cee17 ]
keyring_get_key_chunk() loads description bytes into the index chunk low address first, while keyring_diff_objects() numbers the first differing bit from the low end and folds the absolute byte index into the level without removing the inline-prefix offset the level already carries. The two disagree on byte order and bit position, so the array can be told two keys first differ at a bit that does not differ in the chunk the walker uses, letting crafted descriptions collide into one node.
Load the chunk in the order keyring_diff_objects() assumes and drop the inline-prefix length when folding the byte index into the level. This only changes the in-memory ordering used to place keys within a keyring; add, search and read of non-colliding keys are unaffected.
--- security/keys/keyring.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
security/keys/keyring.c +3 -2
@@ -293,9 +293,10 @@ static unsigned long keyring_get_key_chunk(const void *data, int level) desc_len -= offset;if (desc_len > n) desc_len = n; + d += desc_len;do { chunk <<=8 ; - chunk |= *d++; + chunk |= *--d; }while (--desc_len >0 );return chunk; }
@@ -376,7 +377,7 @@ static int keyring_diff_objects(const void *object, const void *data)return -1 ; differ_plus_i: - level += i; + level += i - (int )sizeof (a->desc); differ: i = level *8 +__ffs (seg_a ^ seg_b);return i;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 257/456 assoc_array: trim the final shortcut word using the current chunk end
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
[ Upstream commit a82c8a05e86f3f84e09698f65b4515b5d04633f6 ]
assoc_array_walk() masks off the bits past shortcut->skip_to_level in the word that contains skip_to_level, gated onround_up(sc_level, ASSOC_ARRAY_KEY_CHUNK_SIZE) > skip_to_level.
That guard is wrong in two opposite ways:
- When sc_level is word-aligned (every word after the first) round_up() is a no-op, so the guard is sc_level > skip_to_level and never fires for the word that holds skip_to_level. A shortcut that spans more than one word and ends in the middle of its last word leaves that word untrimmed, and its stale high bits leak into the dissimilarity word and can steer the walk down the wrong descendant.
- When sc_level is unaligned (the first word) and skip_to_level sits on the next chunk boundary, sc_level + CHUNK would exceed skip_to_level and fire the trim with shift = skip_to_level & CHUNK_MASK == 0, which clears the whole dissimilarity word and makes a differing shortcut compare equal.
Use the end of the chunk that contains sc_level instead:
skip_to_level < round_down(sc_level, CHUNK) + CHUNK
For an aligned sc_level whose word holds skip_to_level this now fires (the first bug); for an unaligned sc_level with skip_to_level on the following boundary it does not, so shift is never 0 when the branch runs and the trim never clears the whole word.
--- lib/assoc_array.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
lib/assoc_array.c +2 -1
@@ -255,7 +255,8 @@ assoc_array_walk(const struct assoc_array *array, sc_segments = shortcut->index_key[sc_level >>ASSOC_ARRAY_KEY_CHUNK_SHIFT ]; dissimilarity = segments ^ sc_segments; -if (round_up (sc_level,ASSOC_ARRAY_KEY_CHUNK_SIZE ) > shortcut->skip_to_level) { +if (shortcut->skip_to_level <round_down (sc_level, +ASSOC_ARRAY_KEY_CHUNK_SIZE ) +ASSOC_ARRAY_KEY_CHUNK_SIZE ) {/* Trim segments that are beyond the shortcut */ int shift = shortcut->skip_to_level &ASSOC_ARRAY_KEY_CHUNK_MASK ; dissimilarity &= ~(ULONG_MAX << shift);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 258/456 netfilter: xt_hashlimit: validate hashtable supports XT_HASHLIMIT_RATE_MATCH
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pablo Neira Ayuso <[email protected]>
[ Upstream commit 305b63e1402267459fdabb183af4527f6799eebf ]
The XT_HASHLIMIT_RATE_MATCH flag mode changes the semantics of the dsthash_ent structure which represents an entry in the hashtable. There is a union area which uses a different layout to express the rate match mode.
Update .checkentry path to validate the XT_HASHLIMIT_RATE_MATCH mode flag is requested by two or more different rules that refer to the same hashtable. Otherwise, uninitialized access to the burst field in the union is possible.
Reject the use of the XT_HASHLIMIT_RATE_MATCH mode flag if set on by revision less than 3 too.
--- net/netfilter/xt_hashlimit.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
net/netfilter/xt_hashlimit.c +15 -1
@@ -118,6 +118,7 @@ struct xt_hashlimit_htable {refcount_t use;u_int8_t family;bool rnd_initialized; +bool ratematch;struct hashlimit_cfg3 cfg;/* config */
@@ -325,6 +326,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg,vfree (hinfo);return -ENOMEM ; } + hinfo->ratematch = !!(cfg->mode &XT_HASHLIMIT_RATE_MATCH );spin_lock_init (&hinfo->lock);switch (revision) {
@@ -868,7 +870,10 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, }/* Check for overflow. */ -if (revision >=3 && cfg->mode &XT_HASHLIMIT_RATE_MATCH ) { +if (cfg->mode &XT_HASHLIMIT_RATE_MATCH ) { +if (revision <3 ) +return -EINVAL ; +if (cfg->avg ==0 || cfg->avg >U32_MAX ) {pr_info_ratelimited ("invalid rate\n" );return -ERANGE ;
@@ -901,6 +906,15 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par,mutex_unlock (&hashlimit_mutex);return ret; } + }else { +if ((cfg->mode &XT_HASHLIMIT_RATE_MATCH && + !(*hinfo)->ratematch) || + (!(cfg->mode &XT_HASHLIMIT_RATE_MATCH ) && + (*hinfo)->ratematch)) { +mutex_unlock (&hashlimit_mutex); +htable_put (*hinfo); +return -EINVAL ; + } }mutex_unlock (&hashlimit_mutex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 259/456 netfilter: nft_payload: fix mask build for partial field offload
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei (Microsoft) <[email protected]>
[ Upstream commit 39e88f28fb32bf02bd4b525c24c842c9cff5663d ]
nft_payload_offload_mask() builds the offload match mask for a payload expression that covers only part of a header field. For a partial IPv6 address match (field_len = 16, priv_len = 1) that shift is 1 << 120, which is undefined on the 32-bit int operand. It also trims only one word, so the remaining words stay 0xffffffff (and when priv_len is a multiple of 4 the trim is skipped entirely), leaving the mask covering more bytes than the rule matches.
UBSAN: shift-out-of-bounds in net/netfilter/nft_payload.c:278:20 shift exponent 120 is too large for 32-bit type 'int' ...
The match is byte-granular and struct nft_data is zero-initialised, so the correct mask is simply the first priv_len bytes set to 0xff. Set those bytes directly and drop the word/shift trimming; this removes the undefined shift and no longer over-masks the trailing bytes.
--- net/netfilter/nft_payload.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-)
net/netfilter/nft_payload.c +1 -11
@@ -216,9 +216,7 @@ static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr)static bool nft_payload_offload_mask (struct nft_offload_reg *reg,u32 priv_len,u32 field_len) { -unsigned int remainder, delta, k;struct nft_data mask = {}; -__be32 remainder_mask;if (priv_len == field_len) {memset (®->mask,0xff , priv_len);
@@ -227,15 +225,7 @@ static bool nft_payload_offload_mask(struct nft_offload_reg *reg,return false; } -memset (&mask,0xff , field_len); - remainder = priv_len %sizeof (u32); -if (remainder) { - k = priv_len /sizeof (u32); - delta = field_len - priv_len; - remainder_mask =htonl (~((1 << (delta *BITS_PER_BYTE )) -1 )); - mask.data[k] = (__force u32 )remainder_mask; - } - +memset (&mask,0xff , priv_len);memcpy (®->mask, &mask, field_len);return true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 260/456 rds: Fix inet6_addr_lst NULL dereference when IPv6 is disabled
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilia Gavrilov <[email protected]>
[ Upstream commit 9c805e592a29be9e4e61ff1bd567da04aa8fd6f9 ]
When booting with the 'ipv6.disable=1' parameter, inet6_addr_lst is never initialized because inet6_init() exits before addrconf_init() is called to initialize it. An attempt to bind an RDS socket to an ipv6 address results in a crash in __ipv6_chk_addr_and_flags()
KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] RIP: 0010:__ipv6_chk_addr_and_flags+0x1df/0x7e0 Call Trace: <TASK> ipv6_chk_addr+0x3b/0x50 rds_tcp_laddr_check+0x155/0x3b0 [rds_tcp] rds_trans_get_preferred+0x15d/0x2d0 [rds] ? trace_hardirqs_on+0x2d/0x110 rds_bind+0x1433/0x1d60 [rds] ? rds_remove_bound+0xd50/0xd50 [rds] ? aa_af_perm+0x250/0x250 ? __might_fault+0xde/0x190 ? __sys_bind+0x1dc/0x210 __sys_bind+0x1dc/0x210 ? __ia32_sys_socketpair+0x100/0x100 ? restore_fpregs_from_fpstate+0x53/0x100 __x64_sys_bind+0x73/0xb0 ? syscall_enter_from_user_mode+0x1c/0x50 do_syscall_64+0x34/0x80 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 RIP: 0033:0x7f47f8269ea9 </TASK>
The following code reproduces the issue:
struct sockaddr_in6 addr;
s = socket(PF_RDS, SOCK_SEQPACKET, 0);
memset(&addr, 0, sizeof(addr));inet_pton(AF_INET6, ADDRESS, &addr.sin6_addr);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(PORT);
bind(s, &addr, sizeof(addr));
Found by InfoTeCS on behalf of Linux Verification Center (linuxtesting.org) with Syzkaller.
Stable-dep-of: 78f75d632f74 ("rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()")
--- net/rds/ib.c | 4 ++++ net/rds/ib_cm.c | 4 ++++ net/rds/tcp.c | 8 +++++--- 3 files changed, 13 insertions(+), 3 deletions(-)
net/rds/ib.c +4 -0
@@ -431,6 +431,10 @@ static int rds_ib_laddr_check_cm(struct net *net, const struct in6_addr *addr, sa = (struct sockaddr *)&sin; }else {#if IS_ENABLED (CONFIG_IPV6 ) +if (!ipv6_mod_enabled ()) { + ret = -EADDRNOTAVAIL ; + goto out; + }memset (&sin6,0 ,sizeof (sin6)); sin6.sin6_family =AF_INET6 ; sin6.sin6_addr = *addr;
net/rds/ib_cm.c +4 -0
@@ -810,6 +810,10 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, dp = event->param.conn.private_data;if (isv6) {#if IS_ENABLED (CONFIG_IPV6 ) +if (!ipv6_mod_enabled ()) { + err = -EOPNOTSUPP ; + goto out; + } dp_cmn = &dp->ricp_v6.dp_cmn; saddr6 = &dp->ricp_v6.dp_saddr; daddr6 = &dp->ricp_v6.dp_daddr;
net/rds/tcp.c +5 -3
@@ -341,9 +341,11 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,rcu_read_unlock (); }#if IS_ENABLED (CONFIG_IPV6 ) - ret =ipv6_chk_addr (net, addr, dev,0 ); -if (ret) -return 0 ; +if (ipv6_mod_enabled ()) { + ret =ipv6_chk_addr (net, addr, dev,0 ); +if (ret) +return 0 ; + }#endif return -EADDRNOTAVAIL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 261/456 rds: tcp: hold the RCU lock across ipv6_chk_addr() in rds_tcp_laddr_check()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xiang Mei <[email protected]>
[ Upstream commit 78f75d632f74b8de0f081a128588f7c37d0d1164 ]
rds_tcp_laddr_check() looks up a scoped IPv6 interface with dev_get_by_index_rcu(), drops the RCU read-side lock, and only then passes the bare struct net_device * into ipv6_chk_addr().
dev_get_by_index_rcu() only keeps the device alive within the same RCU read-side section. After rcu_read_unlock(), a concurrent RTM_DELLINK can free the net_device; ipv6_chk_addr() then dereferences the stale pointer in __ipv6_chk_addr_and_flags() (e.g. l3mdev_master_dev_rcu(dev)), reading freed memory.
Keep the RCU read-side lock held across the ipv6_chk_addr() call instead of dropping it right after the lookup, so the device cannot be freed while it is in use.
BUG: KASAN: slab-use-after-free in __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) Read of size 8 at addr ffff8880106ec000 by task exploit/153 Call Trace: ... kasan_report (mm/kasan/report.c:595) __ipv6_chk_addr_and_flags (... net/ipv6/addrconf.c:1998) ipv6_chk_addr (net/ipv6/addrconf.c:2031 net/ipv6/addrconf.c:1972) rds_tcp_laddr_check (net/rds/tcp.c:370) rds_bind (net/rds/bind.c:248) __sys_bind (net/socket.c:1920) __x64_sys_bind (net/socket.c:1956) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
--- net/rds/tcp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
net/rds/tcp.c +5 -3
@@ -330,23 +330,25 @@ int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,/* If the scope_id is specified, check only those addresses * hosted on the specified interface. */ +rcu_read_lock ();if (scope_id !=0 ) { -rcu_read_lock (); dev =dev_get_by_index_rcu (net, scope_id);/* scope_id is not valid... */ if (!dev) {rcu_read_unlock ();return -EADDRNOTAVAIL ; } -rcu_read_unlock (); }#if IS_ENABLED (CONFIG_IPV6 )if (ipv6_mod_enabled ()) { ret =ipv6_chk_addr (net, addr, dev,0 ); -if (ret) +if (ret) { +rcu_read_unlock ();return 0 ; + } }#endif +rcu_read_unlock ();return -EADDRNOTAVAIL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 262/456 scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit 98b87885de4b7f605533a2860685f5689fce8e82 ]
iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the target-supplied data segment. The segment carries a 2-byte sense length followed by the sense bytes, so it must hold 2 + senselen bytes, but the bounds check only requires datalen >= senselen:
senselen = get_unaligned_be16(data); if (datalen < senselen) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
A target that returns a SCSI Response whose datalen equals senselen (with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + 2 read up to two bytes past the received data. Those bytes are stale conn->data contents and end up in the command's sense buffer, which is returned to userspace.
Account for the 2-byte sense length prefix in the check.
--- drivers/scsi/libiscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/libiscsi.c +1 -1
@@ -866,7 +866,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, } senselen =get_unaligned_be16 (data); -if (datalen < senselen) +if (datalen < senselen +2 ) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 263/456 scsi: libiscsi_tcp: Bound SCSI Response data segment to the connection buffer
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit c1dea15f819cded9b3faf58f8bec72323568b6e6 ]
iscsi_tcp_hdr_dissect() receives the data segment of several PDU types into the fixed-size conn->data buffer, which is allocated for ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU whose DataSegmentLength exceeds that buffer.
The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its data segment (sense/response data) into conn->data via iscsi_tcp_data_recv_prep(), but it does so without the same check. The only upstream bound on in.datalen is conn->max_recv_dlength, the initiator's advertised MaxRecvDataSegmentLength, which is commonly negotiated well above 8192 (open-iscsi defaults to 262144). A target that returns a SCSI Response with a DataSegmentLength between 8193 and max_recv_dlength therefore overflows the 8192-byte conn->data buffer.
Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly like those responses: bound the data segment, receive it into conn->data when present, and otherwise complete the PDU with no data. Fold the opcode into that case group rather than duplicating the check.
--- drivers/scsi/libiscsi_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
drivers/scsi/libiscsi_tcp.c +1 -7
@@ -759,13 +759,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) rc =__iscsi_complete_pdu (conn, hdr,NULL ,0 );spin_unlock (&conn->session->back_lock);break ; -case ISCSI_OP_SCSI_CMD_RSP : -if (tcp_conn->in.datalen) { -iscsi_tcp_data_recv_prep (tcp_conn); -return 0 ; - } - rc =iscsi_complete_pdu (conn, hdr,NULL ,0 ); -break ;case ISCSI_OP_R2T :if (ahslen) { rc =ISCSI_ERR_AHSLEN ;
@@ -773,6 +766,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) } rc =iscsi_tcp_r2t_rsp (conn, hdr);break ; +case ISCSI_OP_SCSI_CMD_RSP :case ISCSI_OP_LOGIN_RSP :case ISCSI_OP_TEXT_RSP :case ISCSI_OP_REJECT :
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 264/456 smb: client: fix buffer leaks in SMB1 read and write
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dawei Feng <[email protected]>
[ Upstream commit 6a3e16d60e81a4aa3056ab15617036cfbea2e07d ]
CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request buffer before checking whether tcon->ses->server is NULL. If that defensive check ever fails, the helper returns -ECONNABORTED without releasing the request buffer.
Fix these leaks by releasing the allocated request buffer before returning from these error paths. Use cifs_small_buf_release() for the buffers allocated by small_smb_init() and cifs_buf_release() for the buffer allocated by smb_init().
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.1.
An x86_64 allyesconfig build showed no new warnings.
Runtime validation used a temporary fault-injection hook to force tcon->ses->server to NULL after request-buffer initialization. On the unfixed kernel, the harness observed two leaked small request buffers and one leaked large request buffer, with directed kmemleak dumps confirming the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer deltas remained.
--- fs/cifs/cifssmb.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
fs/cifs/cifssmb.c +9 -3
@@ -1661,8 +1661,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh =cpu_to_le16 ((__u16)(pid >>16 ));/* tcon and ses pointer are checked in smb_init */ -if (tcon->ses->server ==NULL ) +if (!tcon->ses->server) { +cifs_small_buf_release (pSMB);return -ECONNABORTED ; + } pSMB->AndXCommand =0xFF ;/* none */ pSMB->Fid = netfid;
@@ -1774,8 +1776,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh =cpu_to_le16 ((__u16)(pid >>16 ));/* tcon and ses pointer are checked in smb_init */ -if (tcon->ses->server ==NULL ) +if (!tcon->ses->server) { +cifs_buf_release (pSMB);return -ECONNABORTED ; + } pSMB->AndXCommand =0xFF ;/* none */ pSMB->Fid = netfid;
@@ -2205,8 +2209,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh =cpu_to_le16 ((__u16)(pid >>16 ));/* tcon and ses pointer are checked in smb_init */ -if (tcon->ses->server ==NULL ) +if (!tcon->ses->server) { +cifs_small_buf_release (pSMB);return -ECONNABORTED ; + } pSMB->AndXCommand =0xFF ;/* none */ pSMB->Fid = netfid;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 265/456 hwmon: (nct6775-core) Prevent access to unsupported weight registers
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit d0b704e569ac3b8416d8e02270cdc9bf830ed395 ]
Sashiko reports:
During initialization of the nct6116 chip, the driver sets data->pwm_num to 5. However, it assigns several NCT6106 register arrays (such as NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. These arrays only contain 3 elements.
In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If data->has_pwm has bits 3 or 4 set (which is structurally possible for nct6116), the loop attempts to read elements at index 3 and 4 from these 3-element arrays. This results in a global out-of-bounds read, which can be caught by KASAN.
Furthermore, the driver uses these garbage out-of-bounds values as hardware register addresses for subsequent read and write operations. This leads to invalid hardware register access, potentially causing hardware misconfiguration or system crashes.
The underlying problem is that the chip does support up to five fan control channels, but only the first three support weight control. Fix the problem by extending the affected weight register arrays with zeroed fields. The driver uses zeroed register addresses to determine if a register is supported or not, and skips accesses for unsupported registers.
--- drivers/hwmon/nct6775.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/hwmon/nct6775.c +6 -6
@@ -851,12 +851,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };static const u16 NCT6106_REG_TARGET [] = {0x111 ,0x121 ,0x131 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_SEL [] = {0x168 ,0x178 ,0x188 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP [] = {0x169 ,0x179 ,0x189 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL [] = {0x16a ,0x17a ,0x18a }; -static const u16 NCT6106_REG_WEIGHT_DUTY_STEP [] = {0x16b ,0x17b ,0x18b }; -static const u16 NCT6106_REG_WEIGHT_TEMP_BASE [] = {0x16c ,0x17c ,0x18c }; -static const u16 NCT6106_REG_WEIGHT_DUTY_BASE [] = {0x16d ,0x17d ,0x18d }; +static const u16 NCT6106_REG_WEIGHT_TEMP_SEL [] = {0x168 ,0x178 ,0x188 ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP [] = {0x169 ,0x179 ,0x189 ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL [] = {0x16a ,0x17a ,0x18a ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_STEP [] = {0x16b ,0x17b ,0x18b ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_BASE [] = {0x16c ,0x17c ,0x18c ,0 ,0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_BASE [] = {0x16d ,0x17d ,0x18d ,0 ,0 };static const u16 NCT6106_REG_AUTO_TEMP [] = {0x160 ,0x170 ,0x180 };static const u16 NCT6106_REG_AUTO_PWM [] = {0x164 ,0x174 ,0x184 };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 266/456 net: bridge: mrp: fix Option TLV length in MRP_Test frames
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Corvaglia <[email protected]>
[ Upstream commit 5546da86894d5906f131b05890705a7abf949d84 ]
oui is a pointer, so sizeof(oui) is the pointer size. The MRA Option TLV thus advertises a wrong length (15 vs 10 on x86_64), causing misparsing of the frame on peers. Fix is to replace with sizeof(*oui).
--- net/bridge/br_mrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/bridge/br_mrp.c +1 -1
@@ -215,7 +215,7 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp,struct br_mrp_oui_hdr *oui =NULL ;u8 length; - length =sizeof (*sub_opt) +sizeof (*sub_tlv) +sizeof (oui) + + length =sizeof (*sub_opt) +sizeof (*sub_tlv) +sizeof (*oui) +MRP_OPT_PADDING ;br_mrp_skb_tlv (skb,BR_MRP_TLV_HEADER_OPTION , length);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 267/456 forcedeth: fix UAF of txrx_stats in nv_remove
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chenguang Zhao <[email protected]>
[ Upstream commit 22666ba1420164753d7b0f5a841986b25ace5435 ]
nv_remove() frees the per-CPU txrx_stats before unregister_netdev(). Until unregister completes, ndo_get_stats64, the NAPI/xmit data path, and nv_close()/drain may still access txrx_stats, leading to a use-after-free.
Free the stats only after unregister_netdev().
--- drivers/net/ethernet/nvidia/forcedeth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/net/ethernet/nvidia/forcedeth.c +2 -2
@@ -6190,10 +6190,10 @@ static void nv_remove(struct pci_dev *pci_dev)struct net_device *dev =pci_get_drvdata (pci_dev);struct fe_priv *np =netdev_priv (dev); -free_percpu (np->txrx_stats); -unregister_netdev (dev); +free_percpu (np->txrx_stats); +nv_restore_mac_addr (pci_dev);/* restore any phy related changes */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 268/456 hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 625a2c02a1c04571232a746fe188b4d9a8d63edd ]
During adt7470_read_temperatures(), the driver temporarily switches the PWM channels to manual mode, performs the temperature collection, and then restores the original configuration registers.
However, if an I2C transaction fails at any point after entering manual mode, the function aborts and returns immediately. This leaves the configuration registers un-restored, permanently trapping the fans in manual mode.
Introduce a recovery path to ensure that the original PWM configuration registers are always restored, even when intermediate I2C operations fail.
--- drivers/hwmon/adt7470.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-)
drivers/hwmon/adt7470.c +29 -11
@@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in/* Probe for temperature sensors. Assumes lock is held */ static int adt7470_read_temperatures (struct adt7470_data *data) { -unsigned long res; +struct device *dev =regmap_get_device (data->regmap); +u8 pwm[ADT7470_FAN_COUNT ];unsigned int pwm_cfg[2 ]; -int err; +unsigned long res; +int err, err2;int i; -u8 pwm[ADT7470_FAN_COUNT ];/* save pwm[1-4] config register */ err =regmap_read (data->regmap,ADT7470_REG_PWM_CFG (0 ), &pwm_cfg[0 ]);
@@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err =regmap_update_bits (data->regmap,ADT7470_REG_PWM_CFG (2 ),ADT7470_PWM_AUTO_MASK ,0 );if (err <0 ) -return err; + goto out_restore;/* write pwm control to whatever it was */ err =regmap_bulk_write (data->regmap,ADT7470_REG_PWM (0 ), &pwm[0 ],ADT7470_PWM_COUNT );if (err <0 ) -return err; + goto out_restore;/* start reading temperature sensors */ err =regmap_update_bits (data->regmap,ADT7470_REG_CFG ,ADT7470_T05_STB_MASK ,ADT7470_T05_STB_MASK );if (err <0 ) -return err; + goto out_restore;/* Delay is 200ms * number of temp sensors. */ res = msleep_interruptible((data->num_temp_sensors >= 0 ?
@@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err =regmap_update_bits (data->regmap,ADT7470_REG_CFG ,ADT7470_T05_STB_MASK ,0 );if (err <0 ) -return err; + goto out_restore; +out_restore:/* restore pwm[1-4] config registers */ - err =regmap_write (data->regmap,ADT7470_REG_PWM_CFG (0 ), pwm_cfg[0 ]); -if (err <0 ) -return err; - err =regmap_write (data->regmap,ADT7470_REG_PWM_CFG (2 ), pwm_cfg[1 ]); + err2 =regmap_write (data->regmap,ADT7470_REG_PWM_CFG (0 ), pwm_cfg[0 ]); +if (err2 <0 ) { +dev_warn_ratelimited (dev, +"failed to restore PWM{1,2} config (%d)\n" , + err2); + +if (!err) + err = err2; + } + + err2 =regmap_write (data->regmap,ADT7470_REG_PWM_CFG (2 ), pwm_cfg[1 ]); +if (err2 <0 ) { +dev_warn_ratelimited (dev, +"failed to restore PWM{3,4} config (%d)\n" , + err2); + +if (!err) + err = err2; + } +if (err <0 )return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 269/456 hwmon: (adt7470) Fix cache updated before hardware write on I2C error
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 05270bd38d9bf88a2f4c212246a8fa29f4032078 ]
adt7470_temp_write() and adt7470_pwm_write() update the driver's cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing the corresponding regmap_write(), and never check whether the write succeeded before committing that update. If the I2C transaction fails, the function correctly propagates the error to the caller, but the cache silently keeps the new value, which was never actually applied to the hardware. Subsequent reads then report a value that does not match the device state.
Reorder both write paths to update the cache only after a successful regmap_write(), so the cache always reflects what was actually written to the hardware.
--- drivers/hwmon/adt7470.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
drivers/hwmon/adt7470.c +9 -5
@@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long vaswitch (attr) {case hwmon_temp_min:mutex_lock (&data->lock); - data->temp_min[channel] = val; err =regmap_write (data->regmap,ADT7470_TEMP_MIN_REG (channel), val); +if (!err) + data->temp_min[channel] = val;mutex_unlock (&data->lock);break ;case hwmon_temp_max:mutex_lock (&data->lock); - data->temp_max[channel] = val; err =regmap_write (data->regmap,ADT7470_TEMP_MAX_REG (channel), val); +if (!err) + data->temp_max[channel] = val;mutex_unlock (&data->lock);break ;default :
@@ -839,9 +841,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long valcase hwmon_pwm_input: val =clamp_val (val,0 ,255 );mutex_lock (&data->lock); - data->pwm[channel] = val; err =regmap_write (data->regmap,ADT7470_REG_PWM (channel), - data->pwm[channel]); + val); +if (!err) + data->pwm[channel] = val;mutex_unlock (&data->lock);break ;case hwmon_pwm_enable:
@@ -855,10 +858,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val val--;mutex_lock (&data->lock); - data->pwm_automatic[channel] = val; err =regmap_update_bits (data->regmap,ADT7470_REG_PWM_CFG (channel), pwm_auto_reg_mask, val ? pwm_auto_reg_mask :0 ); +if (!err) + data->pwm_automatic[channel] = val;mutex_unlock (&data->lock);break ;case hwmon_pwm_freq:
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 270/456 hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit cb0b7f9c43b0abbd422a7e4c2c85e91db429207c ]
When userspace configures 'auto_update_interval' to 0 via sysfs, the background kthread executes schedule_timeout_interruptible(0), which returns immediately.
If 'num_temp_sensors' is concurrently or previously set to 0, the msleep_interruptible() delay inside adt7470_read_temperatures() also becomes 0. This combination forces the background thread into a tight, unbounded busy-loop, hogging the CPU and flooding the I2C bus with a continuous stream of transactions.
Fix this vulnerability by raising the lower limit of the clamp_val in auto_update_interval_store() from 0 to 500 milliseconds. This guarantees a reasonable minimum sleep window between sensor updates, protecting the system from intentional or accidental I2C bus denial of service.
--- drivers/hwmon/adt7470.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/adt7470.c +1 -1
@@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev,if (kstrtol (buf,10 , &temp))return -EINVAL ; - temp =clamp_val (temp,0 ,60000 ); + temp =clamp_val (temp,500 ,60000 );mutex_lock (&data->lock); data->auto_update_interval = temp;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 271/456 hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 ]
During the conversion the alarm callback started interpreting the channel index as an alarm bitmask, resulting in incorrect alarm reporting. Compute the proper alarm bit instead.
--- drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
drivers/hwmon/adt7470.c +17 -2
@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };#define ALARM2 (x) ((x) << 8) +/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and +* 0x72, or BIT(0)..BIT(6) of data->alarm. +* TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and +* 0x73, or BIT(8)..BIT(10) of data->alarm. +*/ +#define TEMP_ALARM_BIT (ch) ({ \ + typeof(ch) _ch = (ch); \ + (1 << (_ch < 7 ? _ch : _ch + 1)); \ +}) + +/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in +* reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. +*/ +#define FAN_ALARM_BIT (ch) (1 << (12 + (ch))) +#define ADT7470_VENDOR 0x41#define ADT7470_DEVICE 0x70/* datasheet only mentions a revision 2 */
@@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va *val =1000 * data->temp_max[channel];break ;case hwmon_temp_alarm: - *val = !!(data->alarm & channel); + *val = !!(data->alarm &TEMP_ALARM_BIT (channel));break ;default :return -EOPNOTSUPP ;
@@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val *val =0 ;break ;case hwmon_fan_alarm: - *val = !!(data->alarm & (1 << (12 + channel))); + *val = !!(data->alarm &FAN_ALARM_BIT (channel));break ;default :return -EOPNOTSUPP ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 272/456 hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit a3850231521b06bbbb18c8ebea100320c14a08be ]
The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are currently defined with swapped bit values.
According to Table 22 of the ADT7470 datasheet, the Fan Control Mode Configuration for register 0x69 follows the exact same bit position layout as register 0x68:
- 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80
- 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40
- 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80
- 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40
Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40.
This typo did not cause any functional bugs because these specific macros are never referenced in the driver code. Instead, the driver correctly applies the configuration by relying on the modulo parity of the channel index (e.g., channel % 2) to selectively apply either ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). Since the bit layout is identical between the two configuration registers, the hardware is currently configured correctly.
Fix the macro definitions to reflect the datasheet accurately and prevent future bugs or confusion during code review and refactoring. As this is a purely cosmetic fix with no functional impact, a backport to stable kernels is not necessary.
--- drivers/hwmon/adt7470.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hwmon/adt7470.c +2 -2
@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };#define ADT7470_PWM1_AUTO_MASK 0x80#define ADT7470_PWM_AUTO_MASK 0xC0#define ADT7470_REG_PWM34_CFG 0x69 -#define ADT7470_PWM3_AUTO_MASK 0x40 -#define ADT7470_PWM4_AUTO_MASK 0x80 +#define ADT7470_PWM4_AUTO_MASK 0x40 +#define ADT7470_PWM3_AUTO_MASK 0x80#define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 273/456 hwmon: (adt7470) Use cached PWM frequency value
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 60677cd4c28f44d5b307d3029dccece38fcce90f ]
adt7470_pwm_read() currently ignores failures returned by pwm1_freq_get(). If the register read fails, the negative error code is returned through *val while the function itself reports success, potentially exposing a negative PWM frequency through sysfs.
Fix this by using the cached PWM frequency maintained by the driver, eliminating the register access from the read path.
Apart from the corrected error propagation and using the cached value, no functional change is intended.
--- drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
drivers/hwmon/adt7470.c +18 -2
@@ -182,6 +182,7 @@ struct adt7470_data {u8 pwm_min[ADT7470_PWM_COUNT ];s8 pwm_tmin[ADT7470_PWM_COUNT ];u8 pwm_auto_temp[ADT7470_PWM_COUNT ]; +u32 pwm_freq;struct task_struct *auto_update;unsigned int auto_update_interval;
@@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, }/* These are the valid PWM frequencies to the nearest Hz */ -static const int adt7470_freq_map[] = { +static const u32 adt7470_freq_map[] = {11 ,15 ,22 ,29 ,35 ,44 ,59 ,88 ,1400 ,22500 };
@@ -804,7 +805,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val *val =1 + data->pwm_automatic[channel];break ;case hwmon_pwm_freq: - *val =pwm1_freq_get (dev); + *val = data->pwm_freq;break ;default :return -EOPNOTSUPP ;
@@ -817,12 +818,14 @@ static int pwm1_freq_set(struct device *dev, long freq) {struct adt7470_data *data =dev_get_drvdata (dev);unsigned int low_freq =ADT7470_CFG_LF ; +u32 closest_freq;int index;int err;/* Round the user value given to the closest available frequency */ index =find_closest (freq, adt7470_freq_map,ARRAY_SIZE (adt7470_freq_map)); + closest_freq = adt7470_freq_map[index];if (index >=8 ) { index -=8 ;
@@ -840,6 +843,10 @@ static int pwm1_freq_set(struct device *dev, long freq) err =regmap_update_bits (data->regmap,ADT7470_REG_CFG_2 ,ADT7470_FREQ_MASK , index <<ADT7470_FREQ_SHIFT ); +if (err <0 ) + goto out; + + data->pwm_freq = closest_freq; out:mutex_unlock (&data->lock);
@@ -1293,6 +1300,7 @@ static int adt7470_probe(struct i2c_client *client)struct device *dev = &client->dev;struct adt7470_data *data;struct device *hwmon_dev; +int freq_val;int err; data =devm_kzalloc (dev,sizeof (struct adt7470_data ),GFP_KERNEL );
@@ -1317,6 +1325,14 @@ static int adt7470_probe(struct i2c_client *client)if (err <0 )return err; + freq_val =pwm1_freq_get (dev); +if (freq_val <=0 ) { + err = freq_val <0 ? freq_val : -EINVAL ; +return err; + } + + data->pwm_freq = (u32 )freq_val; +/* Register sysfs hooks */ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &adt7470_chip_info,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 274/456 hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 ]
If the fan data becomes 0 between the FAN_DATA_VALID() check and the FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash due to a race with a concurrent update of the cached fan value.
Fix a TOCTOU issue by reading fan data once.
--- drivers/hwmon/adt7470.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-)
drivers/hwmon/adt7470.c +10 -13
@@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,static int adt7470_fan_read (struct device *dev,u32 attr,int channel,long *val) {struct adt7470_data *data =adt7470_update_device (dev); +u16 fan_data;if (IS_ERR (data))return PTR_ERR (data);switch (attr) {case hwmon_fan_input: -if (FAN_DATA_VALID (data->fan[channel])) - *val =FAN_PERIOD_TO_RPM (data->fan[channel]); -else - *val =0 ; + fan_data =READ_ONCE (data->fan[channel]);break ;case hwmon_fan_min: -if (FAN_DATA_VALID (data->fan_min[channel])) - *val =FAN_PERIOD_TO_RPM (data->fan_min[channel]); -else - *val =0 ; + fan_data =READ_ONCE (data->fan_min[channel]);break ;case hwmon_fan_max: -if (FAN_DATA_VALID (data->fan_max[channel])) - *val =FAN_PERIOD_TO_RPM (data->fan_max[channel]); -else - *val =0 ; + fan_data =READ_ONCE (data->fan_max[channel]);break ;case hwmon_fan_alarm: *val = !!(data->alarm &FAN_ALARM_BIT (channel)); -break ; +return 0 ;default :return -EOPNOTSUPP ; } +if (FAN_DATA_VALID (fan_data)) + *val =FAN_PERIOD_TO_RPM (fan_data); +else + *val =0 ; +return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 275/456 hwmon: (adt7470) Fix PWM auto temp state array and bounds check
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Angelo Daros de Luca <[email protected]>
[ Upstream commit 92413f439d1ec5e55b73ede8d66a7b971cbd1ced ]
In pwm_auto_temp_store(), the parsed user input was missing bounds checks, allowing values > 0xF to overflow into the adjacent channel's bits. Furthermore, the value was being incorrectly written to the pwm_automatic state array instead of pwm_auto_temp.
Fix this by rejecting values > 0xF with -EINVAL, and assigning the value to the correct array only after a successful I2C write.
--- drivers/hwmon/adt7470.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/hwmon/adt7470.c +6 -1
@@ -1057,8 +1057,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,if (temp <0 )return temp; +if (temp >0xF ) +return -EINVAL ; +mutex_lock (&data->lock); - data->pwm_automatic[attr->index] = temp;if (!(attr->index %2 )) { mask =0xF0 ;
@@ -1069,6 +1071,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, } err =regmap_update_bits (data->regmap, pwm_auto_reg, mask, val); +if (!err) + data->pwm_auto_temp[attr->index] = temp; +mutex_unlock (&data->lock);return err <0 ? err : count;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 276/456 powerpc/boot: Fix simpleboot CPU node lookup check
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit c824ab65685bb119c6c6a3a200b3428c72862d5a ]
fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly.
--- arch/powerpc/boot/simpleboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/boot/simpleboot.c +1 -1
@@ -68,7 +68,7 @@ void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,/* finally, setup the timebase */ node =fdt_node_offset_by_prop_value (_dtb_start,-1 ,"device_type" ,"cpu" ,sizeof ("cpu" )); -if (!node) +if (node <0 )fatal ("Cannot find cpu node\n" ); timebase =fdt_getprop (_dtb_start, node,"timebase-frequency" , &size);if (timebase && (size ==4 ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 277/456 powerpc/boot: Fix treeboot-currituck CPU node lookup check
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit 43863f6575d2211e8c5157fefb83ad0ad046aab4 ]
fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly.
--- arch/powerpc/boot/treeboot-currituck.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/boot/treeboot-currituck.c +1 -1
@@ -102,7 +102,7 @@ void platform_init(void) node =fdt_node_offset_by_prop_value (_dtb_start,-1 ,"device_type" ,"cpu" ,sizeof ("cpu" )); -if (!node) +if (node <0 )fatal ("Cannot find cpu node\n" ); timebase =fdt_getprop (_dtb_start, node,"timebase-frequency" , &size);if (timebase && (size ==4 ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 278/456 powerpc/boot: Fix treeboot-akebono CPU node lookup check
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
[ Upstream commit b24fc8278b70a9d27ec801a427ab4de9b769d69a ]
fdt_node_offset_by_prop_value() returns a negative error code on failure - fix the check accordingly.
--- arch/powerpc/boot/treeboot-akebono.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/boot/treeboot-akebono.c +1 -1
@@ -146,7 +146,7 @@ void platform_init(char *userdata) node =fdt_node_offset_by_prop_value (_dtb_start,-1 ,"device_type" ,"cpu" ,sizeof ("cpu" )); -if (!node) +if (node <0 )fatal ("Cannot find cpu node\n" ); timebase =fdt_getprop (_dtb_start, node,"timebase-frequency" , &size);if (timebase && (size ==4 ))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 279/456 wifi: mac80211: validate individual TWT params before driver setup
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhao Li <[email protected]>
[ Upstream commit 0502d5077e419427d80f4d46ba95d0067f5fb916 ]
ieee80211_process_rx_twt_action() only partially validates a received S1G TWT setup frame before queueing it.
An individual agreement can therefore reach ieee80211_s1g_rx_twt_setup() with twt->length too short for the full struct ieee80211_twt_params.
The individual path passes twt to drv_add_twt_setup(). Both the tracepoint and the driver callback consume the complete parameters block, not merely req_type. Do not pass a short individual agreement to the driver. Broadcast agreements remain unchanged because they are rejected locally after accessing only req_type.
[edit commit message to not overclaim lack of validation nor
understate driver impact]
--- net/mac80211/s1g.c | 4 ++++ 1 file changed, 4 insertions(+)
net/mac80211/s1g.c +4 -0
@@ -100,6 +100,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.u.s1g.variable;struct ieee80211_twt_params *twt_agrt = (void *)twt->params; +if (!(twt->control &IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST ) && + twt->length <sizeof (twt->control) +sizeof (*twt_agrt)) +return ; + twt_agrt->req_type &=cpu_to_le16 (~IEEE80211_TWT_REQTYPE_REQUEST );/* broadcast TWT not supported yet */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 280/456 hwmon: (pmbus) Fix return value from pmbus_update_byte_data()
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guenter Roeck <[email protected]>
[ Upstream commit a19038a200f18d9e74ac30081797917d0886e16b ]
pmbus_update_byte_data() is supposed to return a negative error code or 0. However, if no change is made to the register, it actually returns the register value. This can result in problems if the calling code explicitly expects to see an error code or 0.
Fix it to return 0 on success or the error code as expected.
--- drivers/hwmon/pmbus/pmbus_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/pmbus/pmbus_core.c +1 -1
@@ -419,7 +419,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,if (tmp != rv) rv =_pmbus_write_byte_data (client, page, reg, tmp); -return rv; +return rv <0 ? rv :0 ; }EXPORT_SYMBOL_NS_GPL (pmbus_update_byte_data,PMBUS );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 281/456 Bluetooth: L2CAP: fix UAF in l2cap_le_connect_rsp
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiale Yao <[email protected]>
[ Upstream commit c4740e7f23ff9a8210198d8b4703259e21b9f69d ]
l2cap_le_connect_rsp() obtains a channel via __l2cap_get_chan_by_ident() but neither holds a reference nor uses l2cap_chan_hold_unless_zero() before locking and operating on it. A concurrent l2cap_chan_del() triggered by a remote disconnect can free the channel between the lookup and l2cap_chan_lock(), causing a use-after-free.
The BR/EDR counterpart l2cap_connect_rsp() and the sibling handler l2cap_le_command_rej() already use l2cap_chan_hold_unless_zero() to safely hold a reference, but l2cap_le_connect_rsp() was left unprotected.
Fix by adding l2cap_chan_hold_unless_zero() after the ident lookup and l2cap_chan_put() on the exit path, consistent with other L2CAP response handlers.
--- net/bluetooth/l2cap_core.c | 5 +++++ 1 file changed, 5 insertions(+)
net/bluetooth/l2cap_core.c +5 -0
@@ -5734,6 +5734,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, goto unlock; } + chan =l2cap_chan_hold_unless_zero (chan); +if (!chan) +return -EBADSLT ; + err =0 ;l2cap_chan_lock (chan);
@@ -5779,6 +5783,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, }l2cap_chan_unlock (chan); +l2cap_chan_put (chan); unlock:mutex_unlock (&conn->chan_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 282/456 net: phylink: put link_gpio if phylink_create fails
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Christian Marangi <[email protected]>
[ Upstream commit 0fe1e3e8f3380d7862296a73b528d164e96c76b8 ]
In phylink_create() if phylink_register_sfp() returns an error, link_gpio obtained by phylink_parse_fixedlink() is never released. While this is a very unlikely scenario, it's worth to fix/handle this.
This was present from the very first implementation of phylink but got relevant only with the introduction of ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network devices and sfp cages") where additional function were added after phylink_parse_fixedlink() making the release of link_gpio needed if such additional function errored out.
While at it, restructure the exit condition of phylink_create() with the goto pattern to reduce code duplication on handling error conditions.
--- drivers/net/phy/phylink.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-)
drivers/net/phy/phylink.c +15 -14
@@ -857,8 +857,8 @@ struct phylink *phylink_create(struct phylink_config *config, }else if (config->type ==PHYLINK_DEV ) { pl->dev = config->dev; }else { -kfree (pl); -return ERR_PTR (-EINVAL ); + ret = -EINVAL ; + goto free_pl; } pl->phy_state.interface = iface;
@@ -881,28 +881,29 @@ struct phylink *phylink_create(struct phylink_config *config,phylink_validate (pl, pl->supported, &pl->link_config); ret =phylink_parse_mode (pl, fwnode); -if (ret <0 ) { -kfree (pl); -return ERR_PTR (ret); - } +if (ret <0 ) + goto free_pl;if (pl->cfg_link_an_mode ==MLO_AN_FIXED ) { ret =phylink_parse_fixedlink (pl, fwnode); -if (ret <0 ) { -kfree (pl); -return ERR_PTR (ret); - } +if (ret <0 ) + goto release_link_gpio; } pl->cur_link_an_mode = pl->cfg_link_an_mode; ret =phylink_register_sfp (pl, fwnode); -if (ret <0 ) { -kfree (pl); -return ERR_PTR (ret); - } +if (ret <0 ) + goto release_link_gpio;return pl; + +release_link_gpio: +if (pl->link_gpio) +gpiod_put (pl->link_gpio); +free_pl: +kfree (pl); +return ERR_PTR (ret); }EXPORT_SYMBOL_GPL (phylink_create);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 283/456 scsi: zfcp: Fix memory leak during adapter release by destroying gid_pn_req
In reply to Greg Kroah-Hartman5.15-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Block <[email protected]>
[ Upstream commit b601fa590e667bd9643feed8c869b6b3e418480d ]
When releasing an adapter we don't free the mempool 'gid_pn_req' that is allocated during the enqueue. This leaks memory:
unreferenced object 0xd8d29297de700 (size 256):
comm "(udev-worker)", pid 2105, jiffies 4294945794
hex dump (first 32 bytes):
00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N.........
ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g...
backtrace (crc 4a5b5da2):
[<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0
[<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0
[<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150
[<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp]
[<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp]
[<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp]
[<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80
[<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390
[<000dc45f643d8238>] online_store+0x298/0x5b0
[<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480
[<000dc45f62c81150>] new_sync_write+0x370/0x4b0
[<000dc45f62c87abe>] vfs_write+0x43e/0x5b0
[<000dc45f62c87ff4>] ksys_write+0x114/0x1f0
[<000dc45f621c4a16>] do_syscall+0x2f6/0x430
[<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0
[<000dc45f64dc2224>] system_call+0x74/0xa0Fix this by destroying the mempool during the adapter's release.
--- drivers/s390/scsi/zfcp_aux.c | 1 + 1 file changed, 1 insertion(+)
drivers/s390/scsi/zfcp_aux.c +1 -0
@@ -254,6 +254,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)static void zfcp_free_low_mem_buffers (struct zfcp_adapter *adapter) {mempool_destroy (adapter->pool.erp_req); +mempool_destroy (adapter->pool.gid_pn_req);mempool_destroy (adapter->pool.scsi_req);mempool_destroy (adapter->pool.scsi_abort);mempool_destroy (adapter->pool.qtcb_pool);
Signature · 1 line
2.53.0