6.12.80-rc1 review
Subsystems (2)
Greg Kroah-Hartman
maintainerPATCH 139/244 ksmbd: fix potencial OOB in get_file_all_info() for compound requests
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Namjae Jeon <[email protected]>
commit beef2634f81f1c086208191f7228bce1d366493d upstream.
When a compound request consists of QUERY_DIRECTORY + QUERY_INFO (FILE_ALL_INFORMATION) and the first command consumes nearly the entire max_trans_size, get_file_all_info() would blindly call smbConvertToUTF16() with PATH_MAX, causing out-of-bounds write beyond the response buffer. In get_file_all_info(), there was a missing validation check for the client-provided OutputBufferLength before copying the filename into FileName field of the smb2_file_all_info structure.
If the filename length exceeds the available buffer space, it could lead to potential buffer overflows or memory corruption during smbConvertToUTF16 conversion. This calculating the actual free buffer size using smb2_calc_max_out_buf_len() and returning -EINVAL if the buffer is insufficient and updating smbConvertToUTF16 to use the actual filename length (clamped by PATH_MAX) to ensure a safe copy operation.
--- fs/smb/server/smb2pdu.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
fs/smb/server/smb2pdu.c +14 -2
@@ -4941,7 +4941,8 @@ static int get_file_all_info(struct ksmbint conv_len;char *filename;u64 time; -int ret; +int ret, buf_free_len, filename_len; +struct smb2_query_info_req *req =ksmbd_req_buf_next (work);if (!(fp->daccess &FILE_READ_ATTRIBUTES_LE )) { ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n",
@@ -4953,6 +4954,16 @@ static int get_file_all_info(struct ksmbif (IS_ERR (filename))return PTR_ERR (filename); + filename_len =strlen (filename); + buf_free_len =smb2_calc_max_out_buf_len (work, + offsetof(struct smb2_query_info_rsp , Buffer) + + offsetof(struct smb2_file_all_info , FileName), +le32_to_cpu (req->OutputBufferLength)); +if (buf_free_len < (filename_len +1 ) *2 ) { +kfree (filename); +return -EINVAL ; + } + ret =vfs_getattr (&fp->filp->f_path, &stat,STATX_BASIC_STATS ,AT_STATX_SYNC_AS_STAT );if (ret) {
@@ -4988,7 +4999,8 @@ static int get_file_all_info(struct ksmb file_info->Mode = fp->coption; file_info->AlignmentRequirement =0 ; conv_len =smbConvertToUTF16 ((__le16 *)file_info->FileName, filename, -PATH_MAX , conn->local_nls,0 ); +min (filename_len,PATH_MAX ), + conn->local_nls,0 ); conv_len *=2 ; file_info->FileNameLength =cpu_to_le32 (conv_len); rsp->OutputBufferLength =
Greg Kroah-Hartman
maintainerPATCH 140/244 ksmbd: fix memory leaks and NULL deref in smb2_lock()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Werner Kasselman <[email protected]>
commit 309b44ed684496ed3f9c5715d10b899338623512 upstream.
smb2_lock() has three error handling issues after list_del() detaches smb_lock from lock_list at no_check_cl:
1) If vfs_lock_file() returns an unexpected error in the non-UNLOCK path, goto out leaks smb_lock and its flock because the out:
handler only iterates lock_list and rollback_list, neither of which contains the detached smb_lock.
2) If vfs_lock_file() returns -ENOENT in the UNLOCK path, goto out leaks smb_lock and flock for the same reason. The error code returned to the dispatcher is also stale.
3) In the rollback path, smb_flock_init() can return NULL on allocation failure. The result is dereferenced unconditionally, causing a kernel NULL pointer dereference. Add a NULL check to prevent the crash and clean up the bookkeeping; the VFS lock itself cannot be rolled back without the allocation and will be released at file or connection teardown.
Fix cases 1 and 2 by hoisting the locks_free_lock()/kfree() to before the if(!rc) check in the UNLOCK branch so all exit paths share one free site, and by freeing smb_lock and flock before goto out in the non-UNLOCK branch. Propagate the correct error code in both cases. Fix case 3 by wrapping the VFS unlock in an if(rlock) guard and adding a NULL check for locks_free_lock(rlock) in the shared cleanup.
Found via call-graph analysis using sqry.
--- fs/smb/server/smb2pdu.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-)
fs/smb/server/smb2pdu.c +19 -10
@@ -7552,14 +7552,15 @@ retry: rc =vfs_lock_file (filp, smb_lock->cmd, flock,NULL ); skip:if (smb_lock->flags &SMB2_LOCKFLAG_UNLOCK ) { +locks_free_lock (flock); +kfree (smb_lock);if (!rc) {ksmbd_debug (SMB ,"File unlocked\n" ); }else if (rc == -ENOENT ) { rsp->hdr.Status =STATUS_NOT_LOCKED ; + err = rc; goto out; } -locks_free_lock (flock); -kfree (smb_lock); }else {if (rc ==FILE_LOCK_DEFERRED ) {void **argv;
@@ -7628,6 +7629,9 @@ skip:spin_unlock (&work->conn->llist_lock);ksmbd_debug (SMB ,"successful in taking lock\n" ); }else { +locks_free_lock (flock); +kfree (smb_lock); + err = rc; goto out; } }
@@ -7658,13 +7662,17 @@ out:struct file_lock *rlock =NULL ; rlock =smb_flock_init (filp); - rlock->c.flc_type =F_UNLCK ; - rlock->fl_start = smb_lock->start; - rlock->fl_end = smb_lock->end; - - rc =vfs_lock_file (filp,F_SETLK , rlock,NULL ); -if (rc) -pr_err ("rollback unlock fail : %d\n" , rc); +if (rlock) { + rlock->c.flc_type =F_UNLCK ; + rlock->fl_start = smb_lock->start; + rlock->fl_end = smb_lock->end; + + rc =vfs_lock_file (filp,F_SETLK , rlock,NULL ); +if (rc) +pr_err ("rollback unlock fail : %d\n" , rc); + }else { +pr_err ("rollback unlock alloc failed\n" ); + }list_del (&smb_lock->llist);spin_lock (&work->conn->llist_lock);
@@ -7674,7 +7682,8 @@ out:spin_unlock (&work->conn->llist_lock);locks_free_lock (smb_lock->fl); -locks_free_lock (rlock); +if (rlock) +locks_free_lock (rlock);kfree (smb_lock); } out2:
Greg Kroah-Hartman
maintainerPATCH 141/244 ksmbd: do not expire session on binding failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
commit 9bbb19d21ded7d78645506f20d8c44895e3d0fb9 upstream.
When a multichannel session binding request fails (e.g. wrong password), the error path unconditionally sets sess->state = SMB2_SESSION_EXPIRED. However, during binding, sess points to the target session looked up via ksmbd_session_lookup_slowpath() -- which belongs to another connection's user. This allows a remote attacker to invalidate any active session by simply sending a binding request with a wrong password (DoS).
Fix this by skipping session expiration when the failed request was a binding attempt, since the session does not belong to the current connection. The reference taken by ksmbd_session_lookup_slowpath() is still correctly released via ksmbd_user_session_put().
--- fs/smb/server/smb2pdu.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
fs/smb/server/smb2pdu.c +8 -2
@@ -1945,8 +1945,14 @@ out_err:if (sess->user && sess->user->flags &KSMBD_USER_FLAG_DELAY_SESSION ) try_delay = true; - sess->last_active = jiffies; - sess->state =SMB2_SESSION_EXPIRED ; +/* +* For binding requests, session belongs to another +* connection. Do not expire it. +*/ +if (!(req->Flags &SMB2_SESSION_REQ_FLAG_BINDING )) { + sess->last_active = jiffies; + sess->state =SMB2_SESSION_EXPIRED ; + }ksmbd_user_session_put (sess); work->sess =NULL ;if (try_delay) {
Greg Kroah-Hartman
maintainerPATCH 142/244 ALSA: firewire-lib: fix uninitialized local variable
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexey Nepomnyashih <[email protected]>
commit bb120ad57def62e3f23e3d999c5fbed11f610993 upstream.
Similar to commit d8dc8720468a ("ALSA: firewire-lib: fix uninitialized local variable"), the local variable curr_cycle_time in process_rx_packets() is declared without initialization.
When the tracepoint event is not probed, the variable may appear to be used without being initialized. In practice the value is only relevant when the tracepoint is enabled, however initializing it avoids potential use of an uninitialized value and improves code safety.
Initialize curr_cycle_time to zero.
--- sound/firewire/amdtp-stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/firewire/amdtp-stream.c +1 -1
@@ -1179,7 +1179,7 @@ static void process_rx_packets(struct fwstruct pkt_desc *desc = s->packet_descs_cursor;unsigned int pkt_header_length;unsigned int packets; -u32 curr_cycle_time; +u32 curr_cycle_time =0 ;bool need_hw_irq;int i;
Greg Kroah-Hartman
maintainerPATCH 143/244 ASoC: SOF: ipc4-topology: Allow bytes controls without initial payload
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Ujfalusi <[email protected]>
commit d40a198e2b7821197c5c77b89d0130cc90f400f5 upstream.
It is unexpected, but allowed to have no initial payload for a bytes control and the code is prepared to handle this case, but the size check missed this corner case.
Update the check for minimal size to allow the initial size to be 0.
--- sound/soc/sof/ipc4-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
sound/soc/sof/ipc4-topology.c +1 -1
@@ -2527,7 +2527,7 @@ static int sof_ipc4_control_load_bytes(sreturn -EINVAL ; } -if (scontrol->priv_size <sizeof (struct sof_abi_hdr )) { +if (scontrol->priv_size && scontrol->priv_size <sizeof (struct sof_abi_hdr )) {dev_err (sdev->dev,"bytes control %s initial data size %zu is insufficient.\n" , scontrol->name, scontrol->priv_size);
Greg Kroah-Hartman
maintainerPATCH 144/244 can: gw: fix OOB heap access in cgw_csum_crc8_rel()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ali Norouzi <[email protected]>
commit b9c310d72783cc2f30d103eed83920a5a29c671a upstream.
cgw_csum_crc8_rel() correctly computes bounds-safe indices via calc_idx():
int from = calc_idx(crc8->from_idx, cf->len);
int to = calc_idx(crc8->to_idx, cf->len);
int res = calc_idx(crc8->result_idx, cf->len);
if (from < 0 || to < 0 || res < 0)
return;However, the loop and the result write then use the raw s8 fields directly instead of the computed variables:
for (i = crc8->from_idx; ...) /* BUG: raw negative index */
cf->data[crc8->result_idx] = ...; /* BUG: raw negative index */With from_idx = to_idx = result_idx = -64 on a 64-byte CAN FD frame, calc_idx(-64, 64) = 0 so the guard passes, but the loop iterates with i = -64, reading cf->data[-64], and the write goes to cf->data[-64]. This write might end up to 56 (7.0-rc) or 40 (<= 6.19) bytes before the start of the canfd_frame on the heap.
The companion function cgw_csum_xor_rel() uses from/to/res correctly throughout; fix cgw_csum_crc8_rel() to match.
Confirmed with KASAN on linux-7.0-rc2:
BUG: KASAN: slab-out-of-bounds in cgw_csum_crc8_rel+0x515/0x5b0 Read of size 1 at addr ffff8880076619c8 by task poc_cgw_oob/62
To configure the can-gw crc8 checksums CAP_NET_ADMIN is needed.
--- net/can/gw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/can/gw.c +3 -3
@@ -374,10 +374,10 @@ static void cgw_csum_crc8_rel(struct canreturn ;if (from <= to) { -for (i = crc8->from_idx; i <= crc8->to_idx; i++) +for (i = from; i <= to; i++) crc = crc8->crctab[crc ^ cf->data[i]]; }else { -for (i = crc8->from_idx; i >= crc8->to_idx; i--) +for (i = from; i >= to; i--) crc = crc8->crctab[crc ^ cf->data[i]]; }
@@ -396,7 +396,7 @@ static void cgw_csum_crc8_rel(struct canbreak ; } - cf->data[crc8->result_idx] = crc ^ crc8->final_xor_val; + cf->data[res] = crc ^ crc8->final_xor_val; } static void cgw_csum_crc8_pos(struct canfd_frame *cf,
Greg Kroah-Hartman
maintainerPATCH 145/244 can: isotp: fix tx.buf use-after-free in isotp_sendmsg()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Hartkopp <[email protected]>
commit 424e95d62110cdbc8fd12b40918f37e408e35a92 upstream.
isotp_sendmsg() uses only cmpxchg() on so->tx.state to serialize access to so->tx.buf. isotp_release() waits for ISOTP_IDLE via wait_event_interruptible() and then calls kfree(so->tx.buf).
If a signal interrupts the wait_event_interruptible() inside close() while tx.state is ISOTP_SENDING, the loop exits early and release proceeds to force ISOTP_SHUTDOWN and continues to kfree(so->tx.buf) while sendmsg may still be reading so->tx.buf for the final CAN frame in isotp_fill_dataframe().
The so->tx.buf can be allocated once when the standard tx.buf length needs to be extended. Move the kfree() of this potentially extended tx.buf to sk_destruct time when either isotp_sendmsg() and isotp_release() are done.
--- net/can/isotp.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)
net/can/isotp.c +18 -6
@@ -1229,12 +1229,6 @@ static int isotp_release(struct socket * so->ifindex =0 ; so->bound =0 ; -if (so->rx.buf != so->rx.sbuf) -kfree (so->rx.buf); - -if (so->tx.buf != so->tx.sbuf) -kfree (so->tx.buf); -sock_orphan (sk); sock->sk =NULL ;
@@ -1602,6 +1596,21 @@ static int isotp_notifier(struct notifiereturn NOTIFY_DONE ; } +static void isotp_sock_destruct (struct sock *sk) +{ +struct isotp_sock *so =isotp_sk (sk); + +/* do the standard CAN sock destruct work */ +can_sock_destruct (sk); + +/* free potential extended PDU buffers */ +if (so->rx.buf != so->rx.sbuf) +kfree (so->rx.buf); + +if (so->tx.buf != so->tx.sbuf) +kfree (so->tx.buf); +} +static int isotp_init (struct sock *sk) {struct isotp_sock *so =isotp_sk (sk);
@@ -1648,6 +1657,9 @@ static int isotp_init(struct sock *sk)list_add_tail (&so->notifier, &isotp_notifier_list);spin_unlock (&isotp_notifier_lock); +/* re-assign default can_sock_destruct() reference */ + sk->sk_destruct = isotp_sock_destruct; +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 146/244 cpufreq: conservative: Reset requested_freq on limits change
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Viresh Kumar <[email protected]>
commit 6a28fb8cb28b9eb39a392e531d938a889eacafc5 upstream.
A recently reported issue highlighted that the cached requested_freq is not guaranteed to stay in sync with policy->cur. If the platform changes the actual CPU frequency after the governor sets one (e.g. due to platform-specific frequency scaling) and a re-sync occurs later, policy->cur may diverge from requested_freq.
This can lead to incorrect behavior in the conservative governor. For example, the governor may assume the CPU is already running at the maximum frequency and skip further increases even though there is still headroom.
Avoid this by resetting the cached requested_freq to policy->cur on detecting a change in policy limits.
--- drivers/cpufreq/cpufreq_conservative.c | 12 ++++++++++++ drivers/cpufreq/cpufreq_governor.c | 3 +++ drivers/cpufreq/cpufreq_governor.h | 1 + 3 files changed, 16 insertions(+)
drivers/cpufreq/cpufreq_conservative.c +12 -0
@@ -313,6 +313,17 @@ static void cs_start(struct cpufreq_poli dbs_info->requested_freq = policy->cur; } +static void cs_limits (struct cpufreq_policy *policy) +{ +struct cs_policy_dbs_info *dbs_info =to_dbs_info (policy->governor_data); + +/* +* The limits have changed, so may have the current frequency. Reset +* requested_freq to avoid any unintended outcomes due to the mismatch. +*/ + dbs_info->requested_freq = policy->cur; +} +static struct dbs_governor cs_governor = { .gov =CPUFREQ_DBS_GOVERNOR_INITIALIZER ("conservative" ), .kobj_type = { .default_groups = cs_groups },
@@ -322,6 +333,7 @@ static struct dbs_governor cs_governor = .init = cs_init, .exit = cs_exit, .start = cs_start, + .limits = cs_limits, };#define CPU_FREQ_GOV_CONSERVATIVE (cs_governor.gov)
drivers/cpufreq/cpufreq_governor.c +3 -0
@@ -563,6 +563,7 @@ EXPORT_SYMBOL_GPL(cpufreq_dbs_governor_svoid cpufreq_dbs_governor_limits (struct cpufreq_policy *policy) { +struct dbs_governor *gov =dbs_governor_of (policy);struct policy_dbs_info *policy_dbs;/* Protect gov->gdbs_data against cpufreq_dbs_governor_exit() */
@@ -574,6 +575,8 @@ void cpufreq_dbs_governor_limits(structmutex_lock (&policy_dbs->update_mutex);cpufreq_policy_apply_limits (policy);gov_update_sample_delay (policy_dbs,0 ); +if (gov->limits) + gov->limits (policy);mutex_unlock (&policy_dbs->update_mutex); out:
drivers/cpufreq/cpufreq_governor.h +1 -0
@@ -138,6 +138,7 @@ struct dbs_governor {int (*init)(struct dbs_data *dbs_data);void (*exit)(struct dbs_data *dbs_data);void (*start)(struct cpufreq_policy *policy); +void (*limits)(struct cpufreq_policy *policy); };static inline struct dbs_governor *dbs_governor_of (struct cpufreq_policy *policy)
Greg Kroah-Hartman
maintainerPATCH 147/244 platform/x86: ISST: Correct locked bit width
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Srinivas Pandruvada <[email protected]>
commit fbddf68d7b4e1e6da7a78dd7fbd8ec376536584a upstream.
SST-PP locked bit width is set to three bits. It should be only one bit. Use SST_PP_LOCK_WIDTH define instead of SST_PP_LEVEL_WIDTH.
--- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +1 -1
@@ -866,7 +866,7 @@ static int isst_if_get_perf_level(void __read_pp_info ("current_level" , perf_level.current_level,SST_PP_STATUS_OFFSET , SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE)_read_pp_info ("locked" , perf_level.locked,SST_PP_STATUS_OFFSET , - SST_PP_LOCK_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) + SST_PP_LOCK_START, SST_PP_LOCK_WIDTH, SST_MUL_FACTOR_NONE)_read_pp_info ("feature_state" , perf_level.feature_state,SST_PP_STATUS_OFFSET ,SST_PP_FEATURE_STATE_START ,SST_PP_FEATURE_STATE_WIDTH ,SST_MUL_FACTOR_NONE ) perf_level.enabled = !!(power_domain_info->sst_header.cap_mask & BIT(1));
Greg Kroah-Hartman
maintainerPATCH 148/244 KVM: arm64: Discard PC update state on vcpu reset
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marc Zyngier <[email protected]>
commit 1744a6ef48b9a48f017e3e1a0d05de0a6978396e upstream.
Our vcpu reset suffers from a particularly interesting flaw, as it does not correctly deal with state that will have an effect on the execution flow out of reset.
Take the following completely random example, never seen in the wild and that never resulted in a couple of sleepless nights: /s
- vcpu-A issues a PSCI_CPU_OFF using the SMC conduit
- SMC being a trapped instruction (as opposed to HVC which is always normally executed), we annotate the vcpu as needing to skip the next instruction, which is the SMC itself
- vcpu-A is now safely off
- vcpu-B issues a PSCI_CPU_ON for vcpu-A, providing a starting PC
- vcpu-A gets reset, get the new PC, and is sent on its merry way
- right at the point of entering the guest, we notice that a PC increment is pending (remember the earlier SMC?)
- vcpu-A skips its first instruction...
What could possibly go wrong?
Well, I'm glad you asked. For pKVM as a NV guest, that first instruction is extremely significant, as it indicates whether the CPU is booting or resuming. Having skipped that instruction, nothing makes any sense anymore, and CPU hotplugging fails.
This is all caused by the decoupling of PC update from the handling of an exception that triggers such update, making it non-obvious what affects what when.
Fix this train wreck by discarding all the PC-affecting state on vcpu reset.
--- arch/arm64/kvm/reset.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
arch/arm64/kvm/reset.c +14 -0
@@ -257,6 +257,20 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpkvm_vcpu_set_be (vcpu); *vcpu_pc (vcpu) = target_pc; + +/* +* We may come from a state where either a PC update was +* pending (SMC call resulting in PC being increpented to +* skip the SMC) or a pending exception. Make sure we get +* rid of all that, as this cannot be valid out of reset. +* +* Note that clearing the exception mask also clears PC +* updates, but that's an implementation detail, and we +* really want to make it explicit. +*/ +vcpu_clear_flag (vcpu,PENDING_EXCEPTION ); +vcpu_clear_flag (vcpu,EXCEPT_MASK ); +vcpu_clear_flag (vcpu,INCREMENT_PC );vcpu_set_reg (vcpu,0 , reset_state.r0); }
Greg Kroah-Hartman
maintainerPATCH 149/244 hwmon: (pmbus/isl68137) Add mutex protection for AVS enable sysfs attributes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sanman Pradhan <[email protected]>
commit 3075a3951f7708da5a8ab47b0b7d068a32f69e58 upstream.
The custom avs0_enable and avs1_enable sysfs attributes access PMBus registers through the exported API helpers (pmbus_read_byte_data, pmbus_read_word_data, pmbus_write_word_data, pmbus_update_byte_data) without holding the PMBus update_lock mutex. These exported helpers do not acquire the mutex internally, unlike the core's internal callers which hold the lock before invoking them.
The store callback is especially vulnerable: it performs a multi-step read-modify-write sequence (read VOUT_COMMAND, write VOUT_COMMAND, then update OPERATION) where concurrent access from another thread could interleave and corrupt the register state.
Add pmbus_lock_interruptible()/pmbus_unlock() around both the show and store callbacks to serialize PMBus register access with the rest of the driver.
--- drivers/hwmon/pmbus/isl68137.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-)
drivers/hwmon/pmbus/isl68137.c +18 -3
@@ -78,7 +78,15 @@ static ssize_t isl68137_avs_enable_show_int page, char *buf) { - int val = pmbus_read_byte_data(client, page, PMBUS_OPERATION); + int val; + + val =pmbus_lock_interruptible (client); +if (val) +return val; + + val =pmbus_read_byte_data (client, page,PMBUS_OPERATION ); + +pmbus_unlock (client);if (val <0 )return val;
@@ -100,6 +108,10 @@ static ssize_t isl68137_avs_enable_store op_val = result ?ISL68137_VOUT_AVS :0 ; + rc =pmbus_lock_interruptible (client); +if (rc) +return rc; +/* * Writes to VOUT setpoint over AVSBus will persist after the VRM is * switched to PMBus control. Switching back to AVSBus control
@@ -111,17 +123,20 @@ static ssize_t isl68137_avs_enable_store rc =pmbus_read_word_data (client, page,0xff ,PMBUS_VOUT_COMMAND );if (rc <0 ) -return rc; + goto unlock; rc =pmbus_write_word_data (client, page,PMBUS_VOUT_COMMAND , rc);if (rc <0 ) -return rc; + goto unlock; } rc =pmbus_update_byte_data (client, page,PMBUS_OPERATION ,ISL68137_VOUT_AVS , op_val); +unlock: +pmbus_unlock (client); +return (rc <0 ) ? rc : count; }
Greg Kroah-Hartman
maintainerPATCH 150/244 hwmon: (peci/cputemp) Fix crit_hyst returning delta instead of absolute temperature
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sanman Pradhan <[email protected]>
commit 0adc752b4f7d82af7bd14f7cad3091b3b5d702ba upstream.
The hwmon sysfs ABI expects tempN_crit_hyst to report the temperature at which the critical condition clears, not the hysteresis delta from the critical limit.
The peci cputemp driver currently returns tjmax - tcontrol for crit_hyst_type, which is the hysteresis margin rather than the corresponding absolute temperature.
Return tcontrol directly, and update the documentation accordingly.
--- Documentation/hwmon/peci-cputemp.rst | 10 ++++++---- drivers/hwmon/peci/cputemp.c | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-)
Documentation/hwmon/peci-cputemp.rst +6 -4
@@ -51,8 +51,9 @@ temp1_max Provides thermal control temp temp1_crit Provides shutdown temperature of the CPU package which is also known as the maximum processor junction temperature, Tjmax or Tprochot. -temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of - the CPU package. +temp1_crit_hyst Provides the hysteresis temperature of the CPU + package. Returns Tcontrol, the temperature at which + the critical condition clears. temp2_label "DTS" temp2_input Provides current temperature of the CPU package scaled
@@ -62,8 +63,9 @@ temp2_max Provides thermal control temp temp2_crit Provides shutdown temperature of the CPU package which is also known as the maximum processor junction temperature, Tjmax or Tprochot. -temp2_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of - the CPU package. +temp2_crit_hyst Provides the hysteresis temperature of the CPU + package. Returns Tcontrol, the temperature at which + the critical condition clears. temp3_label "Tcontrol" temp3_input Provides current Tcontrol temperature of the CPU
drivers/hwmon/peci/cputemp.c +1 -1
@@ -133,7 +133,7 @@ static int get_temp_target(struct peci_c *val = priv->temp.target.tjmax;break ;case crit_hyst_type: - *val = priv->temp.target.tjmax - priv->temp.target.tcontrol; + *val = priv->temp.target.tcontrol;break ;default : ret = -EOPNOTSUPP ;
Greg Kroah-Hartman
maintainerPATCH 151/244 hwmon: (peci/cputemp) Fix off-by-one in cputemp_is_visible()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sanman Pradhan <[email protected]>
commit b0c9d8ae71509f25690d57f2efddebf7f4b12194 upstream.
cputemp_is_visible() validates the channel index against CPUTEMP_CHANNEL_NUMS, but currently uses '>' instead of '>='. As a result, channel == CPUTEMP_CHANNEL_NUMS is not rejected even though valid indices are 0 .. CPUTEMP_CHANNEL_NUMS - 1.
Fix the bounds check by using '>=' so invalid channel indices are rejected before indexing the core bitmap.
--- drivers/hwmon/peci/cputemp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hwmon/peci/cputemp.c +1 -1
@@ -339,7 +339,7 @@ static umode_t cputemp_is_visible(const {const struct peci_cputemp *priv = data; -if (channel >CPUTEMP_CHANNEL_NUMS ) +if (channel >=CPUTEMP_CHANNEL_NUMS )return 0 ;if (channel < channel_core)
Greg Kroah-Hartman
maintainerPATCH 152/244 media: mc, v4l2: serialize REINIT and REQBUFS with req_queue_mutex
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuchan Nam <[email protected]>
commit bef4f4a88b73e4cc550d25f665b8a9952af22773 upstream.
MEDIA_REQUEST_IOC_REINIT can run concurrently with VIDIOC_REQBUFS(0) queue teardown paths. This can race request object cleanup against vb2 queue cancellation and lead to use-after-free reports.
We already serialize request queueing against STREAMON/OFF with req_queue_mutex. Extend that serialization to REQBUFS, and also take the same mutex in media_request_ioctl_reinit() so REINIT is in the same exclusion domain.
This keeps request cleanup and queue cancellation from running in parallel for request-capable devices.
--- drivers/media/mc/mc-request.c | 5 +++++ drivers/media/v4l2-core/v4l2-ioctl.c | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-)
drivers/media/mc/mc-request.c +5 -0
@@ -190,6 +190,8 @@ static long media_request_ioctl_reinit(sstruct media_device *mdev = req->mdev;unsigned long flags; +mutex_lock (&mdev->req_queue_mutex); +spin_lock_irqsave (&req->lock, flags);if (req->state !=MEDIA_REQUEST_STATE_IDLE && req->state !=MEDIA_REQUEST_STATE_COMPLETE ) {
@@ -197,6 +199,7 @@ static long media_request_ioctl_reinit(s"request: %s not in idle or complete state, cannot reinit\n" , req->debug_str);spin_unlock_irqrestore (&req->lock, flags); +mutex_unlock (&mdev->req_queue_mutex);return -EBUSY ; }if (req->access_count) {
@@ -204,6 +207,7 @@ static long media_request_ioctl_reinit(s"request: %s is being accessed, cannot reinit\n" , req->debug_str);spin_unlock_irqrestore (&req->lock, flags); +mutex_unlock (&mdev->req_queue_mutex);return -EBUSY ; } req->state =MEDIA_REQUEST_STATE_CLEANING ;
@@ -214,6 +218,7 @@ static long media_request_ioctl_reinit(sspin_lock_irqsave (&req->lock, flags); req->state =MEDIA_REQUEST_STATE_IDLE ;spin_unlock_irqrestore (&req->lock, flags); +mutex_unlock (&mdev->req_queue_mutex);return 0 ; }
drivers/media/v4l2-core/v4l2-ioctl.c +3 -2
@@ -3069,13 +3069,14 @@ static long __video_do_ioctl(struct file vfh = file->private_data;/* -* We need to serialize streamon/off with queueing new requests. +* We need to serialize streamon/off/reqbufs with queueing new requests. * These ioctls may trigger the cancellation of a streaming * operation, and that should not be mixed with queueing a new * request at the same time. */ if (v4l2_device_supports_requests (vfd->v4l2_dev) && - (cmd ==VIDIOC_STREAMON || cmd ==VIDIOC_STREAMOFF )) { + (cmd ==VIDIOC_STREAMON || cmd ==VIDIOC_STREAMOFF || + cmd ==VIDIOC_REQBUFS )) { req_queue_lock = &vfd->v4l2_dev->mdev->req_queue_mutex;if (mutex_lock_interruptible (req_queue_lock))
Greg Kroah-Hartman
maintainerPATCH 153/244 virtio_net: Fix UAF on dst_ops when IFF_XMIT_DST_RELEASE is cleared and napi_tx is false
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: xietangxin <[email protected]>
commit ba8bda9a0896746053aa97ac6c3e08168729172c upstream.
A UAF issue occurs when the virtio_net driver is configured with napi_tx=N and the device's IFF_XMIT_DST_RELEASE flag is cleared
(e.g., during the configuration of tc route filter rules).
When IFF_XMIT_DST_RELEASE is removed from the net_device, the network stack expects the driver to hold the reference to skb->dst until the packet is fully transmitted and freed. In virtio_net with napi_tx=N, skbs may remain in the virtio transmit ring for an extended period.
If the network namespace is destroyed while these skbs are still pending, the corresponding dst_ops structure has freed. When a subsequent packet is transmitted, free_old_xmit() is triggered to clean up old skbs. It then calls dst_release() on the skb associated with the stale dst_entry. Since the dst_ops (referenced by the dst_entry) has already been freed, a UAF kernel paging request occurs.
fix it by adds skb_dst_drop(skb) in start_xmit to explicitly release the dst reference before the skb is queued in virtio_net.
Call Trace: Unable to handle kernel paging request at virtual address ffff80007e150000 CPU: 2 UID: 0 PID: 6236 Comm: ping Kdump: loaded Not tainted 7.0.0-rc1+ #6 PREEMPT ... percpu_counter_add_batch+0x3c/0x158 lib/percpu_counter.c:98 (P) dst_release+0xe0/0x110 net/core/dst.c:177 skb_release_head_state+0xe8/0x108 net/core/skbuff.c:1177 sk_skb_reason_drop+0x54/0x2d8 net/core/skbuff.c:1255 dev_kfree_skb_any_reason+0x64/0x78 net/core/dev.c:3469 napi_consume_skb+0x1c4/0x3a0 net/core/skbuff.c:1527 __free_old_xmit+0x164/0x230 drivers/net/virtio_net.c:611 [virtio_net] free_old_xmit drivers/net/virtio_net.c:1081 [virtio_net] start_xmit+0x7c/0x530 drivers/net/virtio_net.c:3329 [virtio_net] ...
Reproduction Steps:
NETDEV="enp3s0"
config_qdisc_route_filter() {
tc qdisc del dev $NETDEV root
tc qdisc add dev $NETDEV root handle 1: prio tc filter add dev $NETDEV parent 1:0 \
protocol ip prio 100 route to 100 flowid 1:1
ip route add 192.168.1.100/32 dev $NETDEV realm 100
}
test_ns() {
ip netns add testns
ip link set $NETDEV netns testns
ip netns exec testns ifconfig $NETDEV 10.0.32.46/24 ip netns exec testns ping -c 1 10.0.32.1
ip netns del testns
}
config_qdisc_route_filter
test_ns
sleep 2test_ns
--- drivers/net/virtio_net.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/virtio_net.c +1 -0
@@ -3178,6 +3178,7 @@ static netdev_tx_t start_xmit(struct sk_/* Don't wait up for transmitted skbs to be freed. */ if (!use_napi) {skb_orphan (skb); +skb_dst_drop (skb);nf_reset_ct (skb); }
Greg Kroah-Hartman
maintainerPATCH 154/244 erofs: add GFP_NOIO in the bio completion if needed
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiucheng Xu <[email protected]>
commit c23df30915f83e7257c8625b690a1cece94142a0 upstream.
The bio completion path in the process context (e.g. dm-verity) will directly call into decompression rather than trigger another workqueue context for minimal scheduling latencies, which can then call vm_map_ram() with GFP_KERNEL.
Due to insufficient memory, vm_map_ram() may generate memory swapping I/O, which can cause submit_bio_wait to deadlock in some scenarios.
Trimmed down the call stack, as follows:
f2fs_submit_read_io
submit_bio //bio_list is initialized.
mmc_blk_mq_recovery
z_erofs_endio
vm_map_ram
__pte_alloc_kernel
__alloc_pages_direct_reclaim
shrink_folio_list
__swap_writepage
submit_bio_wait //bio_list is non-NULL, hang!!!
Use memalloc_noio_{save,restore}() to wrap up this path.
--- fs/erofs/zdata.c | 3 +++ 1 file changed, 3 insertions(+)
fs/erofs/zdata.c +3 -0
@@ -1425,6 +1425,7 @@ static void z_erofs_decompress_kickoff(s int bios) {struct erofs_sb_info *const sbi =EROFS_SB (io->sb); +int gfp_flag;/* wake up the caller thread for sync decompression */ if (io->sync) {
@@ -1457,7 +1458,9 @@ static void z_erofs_decompress_kickoff(s sbi->opt.sync_decompress =EROFS_SYNC_DECOMPRESS_FORCE_ON ;return ; } + gfp_flag =memalloc_noio_save ();z_erofs_decompressqueue_work (&io->u.work); +memalloc_noio_restore (gfp_flag); } static void z_erofs_fill_bio_vec(struct bio_vec *bvec,
Greg Kroah-Hartman
maintainerPATCH 155/244 alarmtimer: Fix argument order in alarm_timer_forward()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
commit 5d16467ae56343b9205caedf85e3a131e0914ad8 upstream.
alarm_timer_forward() passes arguments to alarm_forward() in the wrong order:
alarm_forward(alarm, timr->it_interval, now);
However, alarm_forward() is defined as:
u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval);
and uses the second argument as the current time:
delta = ktime_sub(now, alarm->node.expires);
Passing the interval as "now" results in incorrect delta computation, which can lead to missed expirations or incorrect overrun accounting.
This issue has been present since the introduction of alarm_timer_forward().
Fix this by swapping the arguments.
--- kernel/time/alarmtimer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/time/alarmtimer.c +1 -1
@@ -614,7 +614,7 @@ static s64 alarm_timer_forward(struct k_ {struct alarm *alarm = &timr->it.alarm.alarmtimer; -return alarm_forward (alarm, timr->it_interval, now); +return alarm_forward (alarm, now, timr->it_interval); }/**
Greg Kroah-Hartman
maintainerPATCH 156/244 x86/cpu: Enable FSGSBASE early in cpu_init_exception_handling()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nikunj A Dadhania <[email protected]>
commit 05243d490bb7852a8acca7b5b5658019c7797a52 upstream.
Move FSGSBASE enablement from identify_cpu() to cpu_init_exception_handling() to ensure it is enabled before any exceptions can occur on both boot and secondary CPUs.
== Background ==
Exception entry code (paranoid_entry()) uses ALTERNATIVE patching based on X86_FEATURE_FSGSBASE to decide whether to use RDGSBASE/WRGSBASE instructions or the slower RDMSR/SWAPGS sequence for saving/restoring GSBASE.
On boot CPU, ALTERNATIVE patching happens after enabling FSGSBASE in CR4. When the feature is available, the code is permanently patched to use RDGSBASE/WRGSBASE, which require CR4.FSGSBASE=1 to execute without triggering
== Boot Sequence ==
Boot CPU (with CR pinning enabled):
trap_init()
cpu_init() <- Uses unpatched code (RDMSR/SWAPGS)
x2apic_setup()
...
arch_cpu_finalize_init()
identify_boot_cpu()
identify_cpu()
cr4_set_bits(X86_CR4_FSGSBASE) # Enables the feature # This becomes part of cr4_pinned_bits
...
alternative_instructions() <- Patches code to use RDGSBASE/WRGSBASE
Secondary CPUs (with CR pinning enabled):
start_secondary()
cr4_init() <- Code already patched, CR4.FSGSBASE=1
set implicitly via cr4_pinned_bits
cpu_init() <- exceptions work because FSGSBASE is
already enabled
Secondary CPU (with CR pinning disabled):
start_secondary()
cr4_init() <- Code already patched, CR4.FSGSBASE=0
cpu_init()
x2apic_setup()
rdmsrq(MSR_IA32_APICBASE) <- Triggers #VC in SNP guests
exc_vmm_communication()
paranoid_entry() <- Uses RDGSBASE with CR4.FSGSBASE=0
(patched code)
...
ap_starting()
identify_secondary_cpu()
identify_cpu()
cr4_set_bits(X86_CR4_FSGSBASE) <- Enables the feature, which is
too late
== CR Pinning ==
Currently, for secondary CPUs, CR4.FSGSBASE is set implicitly through
CR-pinning: the boot CPU sets it during identify_cpu(), it becomes part of cr4_pinned_bits, and cr4_init() applies those pinned bits to secondary CPUs. This works but creates an undocumented dependency between cr4_init() and the pinning mechanism.
== Problem ==
Secondary CPUs boot after alternatives have been applied globally. They execute already-patched paranoid_entry() code that uses RDGSBASE/WRGSBASE instructions, which require CR4.FSGSBASE=1. Upcoming changes to CR pinning behavior will break the implicit dependency, causing secondary CPUs to generate #UD.
This issue manifests itself on AMD SEV-SNP guests, where the rdmsrq() in x2apic_setup() triggers a #VC exception early during cpu_init(). The #VC handler (exc_vmm_communication()) executes the patched paranoid_entry() path. Without CR4.FSGSBASE enabled, RDGSBASE instructions trigger #UD.
== Fix ==
Enable FSGSBASE explicitly in cpu_init_exception_handling() before loading exception handlers. This makes the dependency explicit and ensures both boot and secondary CPUs have FSGSBASE enabled before paranoid_entry() executes.
--- arch/x86/kernel/cpu/common.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
arch/x86/kernel/cpu/common.c +12 -6
@@ -1938,12 +1938,6 @@ static void identify_cpu(struct cpuinfo_setup_smap (c);setup_umip (c); -/* Enable FSGSBASE instructions if available. */ -if (cpu_has (c,X86_FEATURE_FSGSBASE )) { -cr4_set_bits (X86_CR4_FSGSBASE ); - elf_hwcap2 |=HWCAP2_FSGSBASE ; - } -/* * The vendor-specific functions might have changed features. * Now we do "generic changes."
@@ -2286,6 +2280,18 @@ void cpu_init_exception_handling(bool bo/* GHCB needs to be setup to handle #VC. */ setup_ghcb (); +/* +* On CPUs with FSGSBASE support, paranoid_entry() uses +* ALTERNATIVE-patched RDGSBASE/WRGSBASE instructions. Secondary CPUs +* boot after alternatives are patched globally, so early exceptions +* execute patched code that depends on FSGSBASE. Enable the feature +* before any exceptions occur. +*/ +if (cpu_feature_enabled (X86_FEATURE_FSGSBASE )) { +cr4_set_bits (X86_CR4_FSGSBASE ); + elf_hwcap2 |=HWCAP2_FSGSBASE ; + } +if (cpu_feature_enabled (X86_FEATURE_FRED )) {/* The boot CPU has enabled FRED during early boot */ if (!boot_cpu)
Greg Kroah-Hartman
maintainerPATCH 157/244 x86/cpu: Remove X86_CR4_FRED from the CR4 pinned bits mask
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Borislav Petkov (AMD) <[email protected]>
commit 411df123c017169922cc767affce76282b8e6c85 upstream.
Commit in Fixes added the FRED CR4 bit to the CR4 pinned bits mask so that whenever something else modifies CR4, that bit remains set. Which in itself is a perfectly fine idea.
However, there's an issue when during boot FRED is initialized: first on the BSP and later on the APs. Thus, there's a window in time when exceptions cannot be handled.
This becomes particularly nasty when running as SEV-{ES,SNP} or TDX guests which, when they manage to trigger exceptions during that short window described above, triple fault due to FRED MSRs not being set up yet.
See Link tag below for a much more detailed explanation of the situation.
So, as a result, the commit in that Link URL tried to address this shortcoming by temporarily disabling CR4 pinning when an AP is not online yet.
However, that is a problem in itself because in this case, an attack on the kernel needs to only modify the online bit - a single bit in RW memory - and then disable CR4 pinning and then disable SM*P, leading to more and worse things to happen to the system.
So, instead, remove the FRED bit from the CR4 pinning mask, thus obviating the need to temporarily disable CR4 pinning.
If someone manages to disable FRED when poking at CR4, then idt_invalidate() would make sure the system would crash'n'burn on the first exception triggered, which is a much better outcome security-wise.
--- arch/x86/kernel/cpu/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kernel/cpu/common.c +1 -1
@@ -405,7 +405,7 @@ out:/* These bits should not change their value after CPU init is finished. */ static const unsigned long cr4_pinned_mask =X86_CR4_SMEP |X86_CR4_SMAP |X86_CR4_UMIP | -X86_CR4_FSGSBASE |X86_CR4_CET |X86_CR4_FRED ; +X86_CR4_FSGSBASE |X86_CR4_CET ;static DEFINE_STATIC_KEY_FALSE_RO (cr_pinning);static unsigned long cr4_pinned_bits__ro_after_init ;
Greg Kroah-Hartman
maintainerPATCH 158/244 phy: qcom: qmp-ufs: Fix SM8650 PCS table for Gear 4
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Abel Vesa <[email protected]>
commit 81af9e40e2e4e1aa95f09fb34811760be6742c58 upstream.
According to internal documentation, on SM8650, when the PHY is configured in Gear 4, the QPHY_V6_PCS_UFS_PLL_CNTL register needs to have the same value as for Gear 5.
At the moment, there is no board that comes with a UFS 3.x device, so this issue doesn't show up, but with the new Eliza SoC, which uses the same init sequence as SM8650, on the MTP board, the link startup fails with the current Gear 4 PCS table.
So fix that by moving the entry into the PCS generic table instead, while keeping the value from Gear 5 configuration.
--- drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +1 -2
@@ -927,6 +927,7 @@ static const struct qmp_phy_init_tbl sm8QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1 ,0x02 ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1 ,0x43 ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_PCS_CTRL1 ,0xc1 ), +QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_PLL_CNTL ,0x33 ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL ,0x0f ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2 ,0x68 ), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S4, 0x0e),
@@ -936,13 +937,11 @@ static const struct qmp_phy_init_tbl sm8 };static const struct qmp_phy_init_tbl sm8650_ufsphy_g4_pcs[] = { -QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_PLL_CNTL ,0x13 ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY ,0x04 ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY ,0x04 ), };static const struct qmp_phy_init_tbl sm8650_ufsphy_g5_pcs[] = { -QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_PLL_CNTL ,0x33 ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY ,0x05 ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY ,0x05 ),QMP_PHY_INIT_CFG (QPHY_V6_PCS_UFS_RX_HS_G5_SYNC_LENGTH_CAPABILITY ,0x4d ),
Greg Kroah-Hartman
maintainerPATCH 159/244 ovl: fix wrong detection of 32bit inode numbers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Amir Goldstein <[email protected]>
commit 53a7c171e9dd833f0a96b545adcb89bd57387239 upstream.
The implicit FILEID_INO32_GEN encoder was changed to be explicit, so we need to fix the detection.
When mounting overlayfs with upperdir and lowerdir on different ext4 filesystems, the expected kmsg log is:
overlayfs: "xino" feature enabled using 32 upper inode bits.
But instead, since the regressing commit, the kmsg log was:
overlayfs: "xino" feature enabled using 2 upper inode bits.
--- fs/overlayfs/util.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
fs/overlayfs/util.c +4 -1
@@ -84,7 +84,10 @@ int ovl_can_decode_fh(struct super_blockif (!exportfs_can_decode_fh (sb->s_export_op))return 0 ; -return sb->s_export_op->encode_fh ?-1 :FILEID_INO32_GEN ; +if (sb->s_export_op->encode_fh == generic_encode_ino32_fh) +return FILEID_INO32_GEN ; + +return -1 ; }struct dentry *ovl_indexdir (struct super_block *sb)
Greg Kroah-Hartman
maintainerPATCH 160/244 scsi: ibmvfc: Fix OOB access in ibmvfc_discover_targets_done()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tyllis Xu <[email protected]>
commit 61d099ac4a7a8fb11ebdb6e2ec8d77f38e77362f upstream.
A malicious or compromised VIO server can return a num_written value in the discover targets MAD response that exceeds max_targets. This value is stored directly in vhost->num_targets without validation, and is then used as the loop bound in ibmvfc_alloc_targets() to index into disc_buf[], which is only allocated for max_targets entries. Indices at or beyond max_targets access kernel memory outside the DMA-coherent allocation. The out-of-bounds data is subsequently embedded in Implicit Logout and PLOGI MADs that are sent back to the VIO server, leaking kernel memory.
Fix by clamping num_written to max_targets before storing it.
--- drivers/scsi/ibmvscsi/ibmvfc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/scsi/ibmvscsi/ibmvfc.c +2 -1
@@ -4963,7 +4963,8 @@ static void ibmvfc_discover_targets_doneswitch (mad_status) {case IBMVFC_MAD_SUCCESS :ibmvfc_dbg (vhost,"Discover Targets succeeded\n" ); - vhost->num_targets =be32_to_cpu (rsp->num_written); + vhost->num_targets =min_t (u32,be32_to_cpu (rsp->num_written), + max_targets);ibmvfc_set_host_action (vhost,IBMVFC_HOST_ACTION_ALLOC_TGTS );break ;case IBMVFC_MAD_FAILED :
Greg Kroah-Hartman
maintainerPATCH 161/244 scsi: ses: Handle positive SCSI error from ses_recv_diag()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Greg Kroah-Hartman <[email protected]>
commit 7a9f448d44127217fabc4065c5ba070d4e0b5d37 upstream.
ses_recv_diag() can return a positive value, which also means that an error happened, so do not only test for negative values.
--- drivers/scsi/ses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/scsi/ses.c +1 -1
@@ -216,7 +216,7 @@ static unsigned char *ses_get_page2_descunsigned char *type_ptr = ses_dev->page1_types;unsigned char *desc_ptr = ses_dev->page2 +8 ; -if (ses_recv_diag (sdev,2 , ses_dev->page2, ses_dev->page2_len) <0 ) +if (ses_recv_diag (sdev,2 , ses_dev->page2, ses_dev->page2_len))return NULL ;for (i =0 ; i < ses_dev->page1_num_types; i++, type_ptr +=4 ) {
Greg Kroah-Hartman
maintainerPATCH 162/244 net: macb: Move devm_{free,request}_irq() out of spin lock area
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Hao <[email protected]>
commit 317e49358ebbf6390fa439ef3c142f9239dd25fb upstream.
The devm_free_irq() and devm_request_irq() functions should not be executed in an atomic context.
During device suspend, all userspace processes and most kernel threads are frozen. Additionally, we flush all tx/rx status, disable all macb interrupts, and halt rx operations. Therefore, it is safe to split the region protected by bp->lock into two independent sections, allowing devm_free_irq() and devm_request_irq() to run in a non-atomic context. This modification resolves the following lockdep warning:
BUG: sleeping function called from invalid context at kernel/locking/mutex.c:591
in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 501, name: rtcwake
preempt_count: 1, expected: 0
RCU nest depth: 1, expected: 0
7 locks held by rtcwake/501:
#0: ffff0008038c3408 (sb_writers#5){.+.+}-{0:0}, at: vfs_write+0xf8/0x368
#1: ffff0008049a5e88 (&of->mutex#2){+.+.}-{4:4}, at: kernfs_fop_write_iter+0xbc/0x1c8
#2: ffff00080098d588 (kn->active#70){.+.+}-{0:0}, at: kernfs_fop_write_iter+0xcc/0x1c8
#3: ffff800081c84888 (system_transition_mutex){+.+.}-{4:4}, at: pm_suspend+0x1ec/0x290
#4: ffff0008009ba0f8 (&dev->mutex){....}-{4:4}, at: device_suspend+0x118/0x4f0
#5: ffff800081d00458 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire+0x4/0x48
#6: ffff0008031fb9e0 (&bp->lock){-.-.}-{3:3}, at: macb_suspend+0x144/0x558
irq event stamp: 8682
hardirqs last enabled at (8681): [<ffff8000813c7d7c>] _raw_spin_unlock_irqrestore+0x44/0x88
hardirqs last disabled at (8682): [<ffff8000813c7b58>] _raw_spin_lock_irqsave+0x38/0x98
softirqs last enabled at (7322): [<ffff8000800f1b4c>] handle_softirqs+0x52c/0x588
softirqs last disabled at (7317): [<ffff800080010310>] __do_softirq+0x20/0x2c
CPU: 1 UID: 0 PID: 501 Comm: rtcwake Not tainted 7.0.0-rc3-next-20260310-yocto-standard+ #125 PREEMPT
Hardware name: ZynqMP ZCU102 Rev1.1 (DT)
Call trace:
show_stack+0x24/0x38 (C)
__dump_stack+0x28/0x38
dump_stack_lvl+0x64/0x88
dump_stack+0x18/0x24
__might_resched+0x200/0x218
__might_sleep+0x38/0x98
__mutex_lock_common+0x7c/0x1378
mutex_lock_nested+0x38/0x50
free_irq+0x68/0x2b0
devm_irq_release+0x24/0x38
devres_release+0x40/0x80
devm_free_irq+0x48/0x88
macb_suspend+0x298/0x558
device_suspend+0x218/0x4f0
dpm_suspend+0x244/0x3a0
dpm_suspend_start+0x50/0x78
suspend_devices_and_enter+0xec/0x560
pm_suspend+0x194/0x290
state_store+0x110/0x158
kobj_attr_store+0x1c/0x30
sysfs_kf_write+0xa8/0xd0
kernfs_fop_write_iter+0x11c/0x1c8
vfs_write+0x248/0x368
ksys_write+0x7c/0xf8
__arm64_sys_write+0x28/0x40
invoke_syscall+0x4c/0xe8
el0_svc_common+0x98/0xf0
do_el0_svc+0x28/0x40
el0_svc+0x54/0x1e0
el0t_64_sync_handler+0x84/0x130
el0t_64_sync+0x198/0x1a0--- drivers/net/ethernet/cadence/macb_main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
drivers/net/ethernet/cadence/macb_main.c +7 -5
@@ -5410,6 +5410,7 @@ static int __maybe_unused macb_suspend(s/* write IP address into register */ tmp |=MACB_BFEXT (IP ,be32_to_cpu (ifa->ifa_local)); } +spin_unlock_irqrestore (&bp->lock, flags);/* Change interrupt handler and * Enable WoL IRQ on queue 0
@@ -5422,11 +5423,12 @@ static int __maybe_unused macb_suspend(sdev_err (dev,"Unable to request IRQ %d (error %d)\n" , bp->queues[0 ].irq, err); -spin_unlock_irqrestore (&bp->lock, flags);return err; } +spin_lock_irqsave (&bp->lock, flags);queue_writel (bp->queues,IER ,GEM_BIT (WOL ));gem_writel (bp,WOL , tmp); +spin_unlock_irqrestore (&bp->lock, flags); }else { err =devm_request_irq (dev, bp->queues[0 ].irq, macb_wol_interrupt,IRQF_SHARED , netdev->name, bp->queues);
@@ -5434,13 +5436,13 @@ static int __maybe_unused macb_suspend(sdev_err (dev,"Unable to request IRQ %d (error %d)\n" , bp->queues[0 ].irq, err); -spin_unlock_irqrestore (&bp->lock, flags);return err; } +spin_lock_irqsave (&bp->lock, flags);queue_writel (bp->queues,IER ,MACB_BIT (WOL ));macb_writel (bp,WOL , tmp); +spin_unlock_irqrestore (&bp->lock, flags); } -spin_unlock_irqrestore (&bp->lock, flags);enable_irq_wake (bp->queues[0 ].irq); }
@@ -5507,6 +5509,8 @@ static int __maybe_unused macb_resume(stqueue_readl (bp->queues,ISR );if (bp->caps &MACB_CAPS_ISR_CLEAR_ON_WRITE )queue_writel (bp->queues,ISR ,-1 ); +spin_unlock_irqrestore (&bp->lock, flags); +/* Replace interrupt handler on queue 0 */ devm_free_irq (dev, bp->queues[0 ].irq, bp->queues); err = devm_request_irq(dev, bp->queues[0].irq, macb_interrupt,
@@ -5515,10 +5519,8 @@ static int __maybe_unused macb_resume(stdev_err (dev,"Unable to request IRQ %d (error %d)\n" , bp->queues[0 ].irq, err); -spin_unlock_irqrestore (&bp->lock, flags);return err; } -spin_unlock_irqrestore (&bp->lock, flags);disable_irq_wake (bp->queues[0 ].irq);
Greg Kroah-Hartman
maintainerPATCH 163/244 net: macb: Protect access to net_device::ip_ptr with RCU lock
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Hao <[email protected]>
commit baa35a698cea26930679a20a7550bbb4c8319725 upstream.
Access to net_device::ip_ptr and its associated members must be protected by an RCU lock. Since we are modifying this piece of code, let's also move it to execute only when WAKE_ARP is enabled.
To minimize the duration of the RCU lock, a local variable is used to temporarily store the IP address. This change resolves the following
RCU check warning:
WARNING: suspicious RCU usage
7.0.0-rc3-next-20260310-yocto-standard+ #122 Not tainted
-----------------------------
drivers/net/ethernet/cadence/macb_main.c:5944 suspicious rcu_dereference_check() usage!
other info that might help us debug this:
rcu_scheduler_active = 2, debug_locks = 1
5 locks held by rtcwake/518:
#0: ffff000803ab1408 (sb_writers#5){.+.+}-{0:0}, at: vfs_write+0xf8/0x368 #1: ffff0008090bf088 (&of->mutex#2){+.+.}-{4:4}, at: kernfs_fop_write_iter+0xbc/0x1c8 #2: ffff00080098d588 (kn->active#70){.+.+}-{0:0}, at: kernfs_fop_write_iter+0xcc/0x1c8 #3: ffff800081c84888 (system_transition_mutex){+.+.}-{4:4}, at: pm_suspend+0x1ec/0x290 #4: ffff0008009ba0f8 (&dev->mutex){....}-{4:4}, at: device_suspend+0x118/0x4f0
stack backtrace: CPU: 3 UID: 0 PID: 518 Comm: rtcwake Not tainted 7.0.0-rc3-next-20260310-yocto-standard+ #122 PREEMPT Hardware name: ZynqMP ZCU102 Rev1.1 (DT) Call trace: show_stack+0x24/0x38 (C) __dump_stack+0x28/0x38 dump_stack_lvl+0x64/0x88 dump_stack+0x18/0x24 lockdep_rcu_suspicious+0x134/0x1d8 macb_suspend+0xd8/0x4c0 device_suspend+0x218/0x4f0 dpm_suspend+0x244/0x3a0 dpm_suspend_start+0x50/0x78 suspend_devices_and_enter+0xec/0x560 pm_suspend+0x194/0x290 state_store+0x110/0x158 kobj_attr_store+0x1c/0x30 sysfs_kf_write+0xa8/0xd0 kernfs_fop_write_iter+0x11c/0x1c8 vfs_write+0x248/0x368 ksys_write+0x7c/0xf8 __arm64_sys_write+0x28/0x40 invoke_syscall+0x4c/0xe8 el0_svc_common+0x98/0xf0 do_el0_svc+0x28/0x40 el0_svc+0x54/0x1e0 el0t_64_sync_handler+0x84/0x130 el0t_64_sync+0x198/0x1a0
--- drivers/net/ethernet/cadence/macb_main.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-)
drivers/net/ethernet/cadence/macb_main.c +16 -9
@@ -5352,9 +5352,9 @@ static int __maybe_unused macb_suspend(sstruct macb_queue *queue;struct in_device *idev;unsigned long flags; +u32 tmp, ifa_local;unsigned int q;int err; -u32 tmp;if (!device_may_wakeup (&bp->dev->dev))phy_exit (bp->sgmii_phy);
@@ -5363,14 +5363,21 @@ static int __maybe_unused macb_suspend(sreturn 0 ;if (bp->wol &MACB_WOL_ENABLED ) { -/* Check for IP address in WOL ARP mode */ - idev =__in_dev_get_rcu (bp->dev); -if (idev) - ifa =rcu_dereference (idev->ifa_list); -if ((bp->wolopts &WAKE_ARP ) && !ifa) { -netdev_err (netdev,"IP address not assigned as required by WoL walk ARP\n" ); -return -EOPNOTSUPP ; +if (bp->wolopts &WAKE_ARP ) { +/* Check for IP address in WOL ARP mode */ +rcu_read_lock (); + idev =__in_dev_get_rcu (bp->dev); +if (idev) + ifa =rcu_dereference (idev->ifa_list); +if (!ifa) { +rcu_read_unlock (); +netdev_err (netdev,"IP address not assigned as required by WoL walk ARP\n" ); +return -EOPNOTSUPP ; + } + ifa_local =be32_to_cpu (ifa->ifa_local); +rcu_read_unlock (); } +spin_lock_irqsave (&bp->lock, flags);/* Disable Tx and Rx engines before disabling the queues,
@@ -5408,7 +5415,7 @@ static int __maybe_unused macb_suspend(sif (bp->wolopts &WAKE_ARP ) { tmp |=MACB_BIT (ARP );/* write IP address into register */ - tmp |=MACB_BFEXT (IP ,be32_to_cpu (ifa->ifa_local)); + tmp |=MACB_BFEXT (IP , ifa_local); }spin_unlock_irqrestore (&bp->lock, flags);
Greg Kroah-Hartman
maintainerPATCH 164/244 net: macb: Use dev_consume_skb_any() to free TX SKBs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kevin Hao <[email protected]>
commit 647b8a2fe474474704110db6bd07f7a139e621eb upstream.
The napi_consume_skb() function is not intended to be called in an IRQ disabled context. However, after commit 6bc8a5098bf4 ("net: macb: Fix tx_ptr_lock locking"), the freeing of TX SKBs is performed with IRQs disabled. To resolve the following call trace, use dev_consume_skb_any() for freeing TX SKBs:
WARNING: kernel/softirq.c:430 at __local_bh_enable_ip+0x174/0x188, CPU#0: ksoftirqd/0/15
Modules linked in:
CPU: 0 UID: 0 PID: 15 Comm: ksoftirqd/0 Not tainted 7.0.0-rc4-next-20260319-yocto-standard-dirty #37 PREEMPT
Hardware name: ZynqMP ZCU102 Rev1.1 (DT)
pstate: 200000c5 (nzCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : __local_bh_enable_ip+0x174/0x188
lr : local_bh_enable+0x24/0x38
sp : ffff800082b3bb10
x29: ffff800082b3bb10 x28: ffff0008031f3c00 x27: 000000000011ede0
x26: ffff000800a7ff00 x25: ffff800083937ce8 x24: 0000000000017a80
x23: ffff000803243a78 x22: 0000000000000040 x21: 0000000000000000
x20: ffff000800394c80 x19: 0000000000000200 x18: 0000000000000001
x17: 0000000000000001 x16: ffff000803240000 x15: 0000000000000000
x14: ffffffffffffffff x13: 0000000000000028 x12: ffff000800395650
x11: ffff8000821d1528 x10: ffff800081c2bc08 x9 : ffff800081c1e258
x8 : 0000000100000301 x7 : ffff8000810426ec x6 : 0000000000000000
x5 : 0000000000000001 x4 : 0000000000000001 x3 : 0000000000000000
x2 : 0000000000000008 x1 : 0000000000000200 x0 : ffff8000810428dc
Call trace:
__local_bh_enable_ip+0x174/0x188 (P)
local_bh_enable+0x24/0x38
skb_attempt_defer_free+0x190/0x1d8
napi_consume_skb+0x58/0x108
macb_tx_poll+0x1a4/0x558
__napi_poll+0x50/0x198
net_rx_action+0x1f4/0x3d8
handle_softirqs+0x16c/0x560
run_ksoftirqd+0x44/0x80
smpboot_thread_fn+0x1d8/0x338
kthread+0x120/0x150
ret_from_fork+0x10/0x20
irq event stamp: 29751
hardirqs last enabled at (29750): [<ffff8000813be184>] _raw_spin_unlock_irqrestore+0x44/0x88
hardirqs last disabled at (29751): [<ffff8000813bdf60>] _raw_spin_lock_irqsave+0x38/0x98
softirqs last enabled at (29150): [<ffff8000800f1aec>] handle_softirqs+0x504/0x560
softirqs last disabled at (29153): [<ffff8000800f2fec>] run_ksoftirqd+0x44/0x80--- drivers/net/ethernet/cadence/macb_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/ethernet/cadence/macb_main.c +1 -1
@@ -1129,7 +1129,7 @@ static void macb_tx_unmap(struct macb *b }if (tx_skb->skb) { -napi_consume_skb (tx_skb->skb, budget); +dev_consume_skb_any (tx_skb->skb); tx_skb->skb =NULL ; } }
Greg Kroah-Hartman
maintainerPATCH 165/244 KVM: x86/mmu: Drop/zap existing present SPTE even when creating an MMIO SPTE
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit aad885e774966e97b675dfe928da164214a71605 upstream.
When installing an emulated MMIO SPTE, do so *after* dropping/zapping the existing SPTE (if it's shadow-present). While commit a54aa15c6bda3 was right about it being impossible to convert a shadow-present SPTE to an MMIO SPTE due to a _guest_ write, it failed to account for writes to guest memory that are outside the scope of KVM.
E.g. if host userspace modifies a shadowed gPTE to switch from a memslot to emulted MMIO and then the guest hits a relevant page fault, KVM will install the MMIO SPTE without first zapping the shadow-present SPTE.
------------[ cut here ]------------ is_shadow_present_pte(*sptep) WARNING: arch/x86/kvm/mmu/mmu.c:484 at mark_mmio_spte+0xb2/0xc0 [kvm], CPU#0: vmx_ept_stale_r/4292 Modules linked in: kvm_intel kvm irqbypass CPU: 0 UID: 1000 PID: 4292 Comm: vmx_ept_stale_r Not tainted 7.0.0-rc2-eafebd2d2ab0-sink-vm #319 PREEMPT Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 RIP: 0010:mark_mmio_spte+0xb2/0xc0 [kvm] Call Trace: <TASK> mmu_set_spte+0x237/0x440 [kvm] ept_page_fault+0x535/0x7f0 [kvm] kvm_mmu_do_page_fault+0xee/0x1f0 [kvm] kvm_mmu_page_fault+0x8d/0x620 [kvm] vmx_handle_exit+0x18c/0x5a0 [kvm_intel] kvm_arch_vcpu_ioctl_run+0xc55/0x1c20 [kvm] kvm_vcpu_ioctl+0x2d5/0x980 [kvm] __x64_sys_ioctl+0x8a/0xd0 do_syscall_64+0xb5/0x730 entry_SYSCALL_64_after_hwframe+0x4b/0x53 RIP: 0033:0x47fa3f </TASK> ---[ end trace 0000000000000000 ]---
--- arch/x86/kvm/mmu/mmu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
arch/x86/kvm/mmu/mmu.c +8 -6
@@ -2919,12 +2919,6 @@ static int mmu_set_spte(struct kvm_vcpubool prefetch = !fault || fault->prefetch;bool write_fault = fault && fault->write; -if (unlikely (is_noslot_pfn (pfn))) { - vcpu->stat.pf_mmio_spte_created++; -mark_mmio_spte (vcpu, sptep, gfn, pte_access); -return RET_PF_EMULATE ; - } -if (is_shadow_present_pte (*sptep)) {/* * If we overwrite a PTE page pointer with a 2MB PMD, unlink
@@ -2944,6 +2938,14 @@ static int mmu_set_spte(struct kvm_vcpu was_rmapped =1 ; } +if (unlikely (is_noslot_pfn (pfn))) { + vcpu->stat.pf_mmio_spte_created++; +mark_mmio_spte (vcpu, sptep, gfn, pte_access); +if (flush) +kvm_flush_remote_tlbs_gfn (vcpu->kvm, gfn, level); +return RET_PF_EMULATE ; + } + wrprot =make_spte (vcpu, sp, slot, pte_access, gfn, pfn, *sptep, prefetch, true, host_writable, &spte);
Greg Kroah-Hartman
maintainerPATCH 166/244 jbd2: gracefully abort on checkpointing state corruptions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Milos Nikic <[email protected]>
commit bac3190a8e79beff6ed221975e0c9b1b5f2a21da upstream.
This patch targets two internal state machine invariants in checkpoint.c residing inside functions that natively return integer error codes.
- In jbd2_cleanup_journal_tail(): A blocknr of 0 indicates a severely corrupted journal superblock. Replaced the J_ASSERT with a WARN_ON_ONCE and a graceful journal abort, returning -EFSCORRUPTED.
- In jbd2_log_do_checkpoint(): Replaced the J_ASSERT_BH checking for an unexpected buffer_jwrite state. If the warning triggers, we explicitly drop the just-taken get_bh() reference and call __flush_batch() to safely clean up any previously queued buffers in the j_chkpt_bhs array, preventing a memory leak before returning -EFSCORRUPTED.
--- fs/jbd2/checkpoint.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
fs/jbd2/checkpoint.c +13 -2
@@ -267,7 +267,15 @@ restart:*/ BUFFER_TRACE (bh,"queue" );get_bh (bh); -J_ASSERT_BH (bh, !buffer_jwrite (bh)); +if (WARN_ON_ONCE (buffer_jwrite (bh))) { +put_bh (bh);/* drop the ref we just took */ +spin_unlock (&journal->j_list_lock); +/* Clean up any previously batched buffers */ +if (batch_count) +__flush_batch (journal, &batch_count); +jbd2_journal_abort (journal, -EFSCORRUPTED ); +return -EFSCORRUPTED ; + } journal->j_chkpt_bhs[batch_count++] = bh; transaction->t_chp_stats.cs_written++; transaction->t_checkpoint_list = jh->b_cpnext;
@@ -325,7 +333,10 @@ int jbd2_cleanup_journal_tail(journal_tif (!jbd2_journal_get_log_tail (journal, &first_tid, &blocknr))return 1 ; -J_ASSERT (blocknr !=0 ); +if (WARN_ON_ONCE (blocknr ==0 )) { +jbd2_journal_abort (journal, -EFSCORRUPTED ); +return -EFSCORRUPTED ; + }/* * We need to make sure that any blocks that were recently written out
Greg Kroah-Hartman
maintainerPATCH 167/244 irqchip/qcom-mpm: Add missing mailbox TX done acknowledgment
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jassi Brar <[email protected]>
commit cfe02147e86307a17057ee4e3604f5f5919571d2 upstream.
The mbox_client for qcom-mpm sends NULL doorbell messages via mbox_send_message() but never signals TX completion.
Set knows_txdone=true and call mbox_client_txdone() after a successful send, matching the pattern used by other Qualcomm mailbox clients (smp2p, smsm, qcom_aoss etc).
--- drivers/irqchip/irq-qcom-mpm.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/irqchip/irq-qcom-mpm.c +3 -0
@@ -306,6 +306,8 @@ static int mpm_pd_power_off(struct generif (ret <0 )return ret; +mbox_client_txdone (priv->mbox_chan,0 ); +return 0 ; }
@@ -434,6 +436,7 @@ static int qcom_mpm_init(struct device_n } priv->mbox_client.dev = dev; + priv->mbox_client.knows_txdone = true; priv->mbox_chan =mbox_request_channel (&priv->mbox_client,0 );if (IS_ERR (priv->mbox_chan)) { ret =PTR_ERR (priv->mbox_chan);
Greg Kroah-Hartman
maintainerPATCH 168/244 futex: Clear stale exiting pointer in futex_lock_pi() retry path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Davidlohr Bueso <[email protected]>
commit 210d36d892de5195e6766c45519dfb1e65f3eb83 upstream.
Fuzzying/stressing futexes triggered:
WARNING: kernel/futex/core.c:825 at wait_for_owner_exiting+0x7a/0x80, CPU#11: futex_lock_pi_s/524
When futex_lock_pi_atomic() sees the owner is exiting, it returns -EBUSY and stores a refcounted task pointer in 'exiting'.
After wait_for_owner_exiting() consumes that reference, the local pointer is never reset to nil. Upon a retry, if futex_lock_pi_atomic() returns a different error, the bogus pointer is passed to wait_for_owner_exiting().
CPU0 CPU1 CPU2
futex_lock_pi(uaddr)
// acquires the PI futex
exit()
futex_cleanup_begin()
futex_state = EXITING;
futex_lock_pi(uaddr)
futex_lock_pi_atomic()
attach_to_pi_owner()
// observes EXITING
*exiting = owner; // takes ref
return -EBUSY
wait_for_owner_exiting(-EBUSY, owner)
put_task_struct(); // drops ref
// exiting still points to owner
goto retry;
futex_lock_pi_atomic()
lock_pi_update_atomic()
cmpxchg(uaddr)
*uaddr ^= WAITERS // whatever
// value changed
return -EAGAIN;
wait_for_owner_exiting(-EAGAIN, exiting) // stale
WARN_ON_ONCE(exiting)Fix this by resetting upon retry, essentially aligning it with requeue_pi.
--- kernel/futex/pi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/futex/pi.c +2 -1
@@ -918,7 +918,7 @@ int fixup_pi_owner(u32 __user *uaddr, stint futex_lock_pi (u32 __user *uaddr,unsigned int flags,ktime_t *time,int trylock) {struct hrtimer_sleeper timeout, *to; -struct task_struct *exiting =NULL ; +struct task_struct *exiting;struct rt_mutex_waiter rt_waiter;struct futex_hash_bucket *hb;struct futex_q q = futex_q_init;
@@ -933,6 +933,7 @@ int futex_lock_pi(u32 __user *uaddr, uns to =futex_setup_timer (time, &timeout, flags,0 ); retry: + exiting =NULL ; ret =get_futex_key (uaddr, flags, &q.key,FUTEX_WRITE );if (unlikely (ret !=0 )) goto out;
Greg Kroah-Hartman
maintainerPATCH 169/244 dmaengine: fsl-edma: fix channel parameter config for fixed channel requests
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joy Zou <[email protected]>
commit 2e7b5cf72e51c9cf9c8b75190189c757df31ddd9 upstream.
Configure only the requested channel when a fixed channel is specified to avoid modifying other channels unintentionally.
Fix parameter configuration when a fixed DMA channel is requested on i.MX9 AON domain and i.MX8QM/QXP/DXL platforms. When a client requests a fixed channel (e.g., channel 6), the driver traverses channels 0-5 and may unintentionally modify their configuration if they are unused.
This leads to issues such as setting the is_multi_fifo flag unexpectedly, causing memcpy tests to fail when using the dmatest tool.
Only affect edma memcpy test when the channel is fixed.
--- drivers/dma/fsl-edma-main.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-)
drivers/dma/fsl-edma-main.c +11 -15
@@ -185,10 +185,8 @@ static struct dma_chan *fsl_edma3_xlate(return NULL ; i = fsl_chan - fsl_edma->chans; - fsl_chan->priority = dma_spec->args[1 ]; - fsl_chan->is_rxchan = dma_spec->args[2 ] &FSL_EDMA_RX ; - fsl_chan->is_remote = dma_spec->args[2 ] &FSL_EDMA_REMOTE ; - fsl_chan->is_multi_fifo = dma_spec->args[2 ] &FSL_EDMA_MULTI_FIFO ; +if (!b_chmux && i != dma_spec->args[0 ]) +continue ;if ((dma_spec->args[2 ] &FSL_EDMA_EVEN_CH ) && (i &0x1 ))continue ;
@@ -196,17 +194,15 @@ static struct dma_chan *fsl_edma3_xlate(if ((dma_spec->args[2 ] &FSL_EDMA_ODD_CH ) && !(i &0x1 ))continue ; -if (!b_chmux && i == dma_spec->args[0 ]) { - chan =dma_get_slave_channel (chan); - chan->device->privatecnt++; -return chan; - }else if (b_chmux && !fsl_chan->srcid) { -/* if controller support channel mux, choose a free channel */ - chan =dma_get_slave_channel (chan); - chan->device->privatecnt++; - fsl_chan->srcid = dma_spec->args[0 ]; -return chan; - } + fsl_chan->srcid = dma_spec->args[0 ]; + fsl_chan->priority = dma_spec->args[1 ]; + fsl_chan->is_rxchan = dma_spec->args[2 ] &FSL_EDMA_RX ; + fsl_chan->is_remote = dma_spec->args[2 ] &FSL_EDMA_REMOTE ; + fsl_chan->is_multi_fifo = dma_spec->args[2 ] &FSL_EDMA_MULTI_FIFO ; + + chan =dma_get_slave_channel (chan); + chan->device->privatecnt++; +return chan; }return NULL ; }
Greg Kroah-Hartman
maintainerPATCH 170/244 dmaengine: sh: rz-dmac: Protect the driver specific lists
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Claudiu Beznea <[email protected]>
commit abb863e6213dc41a58ef8bb3289b7e77460dabf3 upstream.
The driver lists (ld_free, ld_queue) are used in rz_dmac_free_chan_resources(), rz_dmac_terminate_all(), rz_dmac_issue_pending(), and rz_dmac_irq_handler_thread(), all under the virtual channel lock. Take the same lock in rz_dmac_prep_slave_sg() and rz_dmac_prep_dma_memcpy() as well to avoid concurrency issues, since these functions also check whether the lists are empty and update or remove list entries.
--- drivers/dma/sh/rz-dmac.c | 63 ++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 28 deletions(-)
drivers/dma/sh/rz-dmac.c +35 -28
@@ -10,6 +10,7 @@*/ #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/interrupt.h>
@@ -424,6 +425,7 @@ static int rz_dmac_alloc_chan_resources(if (!desc)break ; +/* No need to lock. This is called only for the 1st client. */ list_add_tail (&desc->node, &channel->ld_free); channel->descs_allocated++; }
@@ -479,18 +481,21 @@ rz_dmac_prep_dma_memcpy(struct dma_chandev_dbg (dmac->dev,"%s channel: %d src=0x%pad dst=0x%pad len=%zu\n" , __func__, channel->index, &src, &dest, len); -if (list_empty (&channel->ld_free)) -return NULL ; + scoped_guard(spinlock_irqsave, &channel->vc.lock) { +if (list_empty (&channel->ld_free)) +return NULL ; + + desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node); + + desc->type =RZ_DMAC_DESC_MEMCPY ; + desc->src = src; + desc->dest = dest; + desc->len = len; + desc->direction =DMA_MEM_TO_MEM ; - desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node); - - desc->type =RZ_DMAC_DESC_MEMCPY ; - desc->src = src; - desc->dest = dest; - desc->len = len; - desc->direction =DMA_MEM_TO_MEM ; +list_move_tail (channel->ld_free.next, &channel->ld_queue); + } -list_move_tail (channel->ld_free.next, &channel->ld_queue);return vchan_tx_prep (&channel->vc, &desc->vd, flags); }
@@ -506,27 +511,29 @@ rz_dmac_prep_slave_sg(struct dma_chan *cint dma_length =0 ;int i =0 ; -if (list_empty (&channel->ld_free)) -return NULL ; - - desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node); + scoped_guard(spinlock_irqsave, &channel->vc.lock) { +if (list_empty (&channel->ld_free)) +return NULL ; + + desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node); + +for_each_sg (sgl, sg, sg_len, i) + dma_length +=sg_dma_len (sg); + + desc->type =RZ_DMAC_DESC_SLAVE_SG ; + desc->sg = sgl; + desc->sgcount = sg_len; + desc->len = dma_length; + desc->direction = direction; + +if (direction ==DMA_DEV_TO_MEM ) + desc->src = channel->src_per_address; +else + desc->dest = channel->dst_per_address; -for_each_sg (sgl, sg, sg_len, i) { - dma_length +=sg_dma_len (sg); +list_move_tail (channel->ld_free.next, &channel->ld_queue); } - desc->type =RZ_DMAC_DESC_SLAVE_SG ; - desc->sg = sgl; - desc->sgcount = sg_len; - desc->len = dma_length; - desc->direction = direction; - -if (direction ==DMA_DEV_TO_MEM ) - desc->src = channel->src_per_address; -else - desc->dest = channel->dst_per_address; - -list_move_tail (channel->ld_free.next, &channel->ld_queue);return vchan_tx_prep (&channel->vc, &desc->vd, flags); }
Greg Kroah-Hartman
maintainerPATCH 171/244 dmaengine: sh: rz-dmac: Move CHCTRL updates under spinlock
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Claudiu Beznea <[email protected]>
commit 89a8567d84bde88cb7cdbbac2ab2299c4f991490 upstream.
Both rz_dmac_disable_hw() and rz_dmac_irq_handle_channel() update the CHCTRL register. To avoid concurrency issues when configuring functionalities exposed by this registers, take the virtual channel lock. All other CHCTRL updates were already protected by the same lock.
Previously, rz_dmac_disable_hw() disabled and re-enabled local IRQs, before accessing CHCTRL registers but this does not ensure race-free access. Remove the local IRQ disable/enable code as well.
--- drivers/dma/sh/rz-dmac.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
drivers/dma/sh/rz-dmac.c +4 -5
@@ -286,13 +286,10 @@ static void rz_dmac_disable_hw(struct rz {struct dma_chan *chan = &channel->vc.chan;struct rz_dmac *dmac =to_rz_dmac (chan->device); -unsigned long flags;dev_dbg (dmac->dev,"%s channel %d\n" , __func__, channel->index); -local_irq_save (flags);rz_dmac_ch_writel (channel,CHCTRL_DEFAULT ,CHCTRL ,1 ); -local_irq_restore (flags); }static void rz_dmac_set_dmars_register (struct rz_dmac *dmac,int nr,u32 dmars)
@@ -545,8 +542,8 @@ static int rz_dmac_terminate_all(structunsigned int i;LIST_HEAD (head); -rz_dmac_disable_hw (channel);spin_lock_irqsave (&channel->vc.lock, flags); +rz_dmac_disable_hw (channel);for (i =0 ; i <DMAC_NR_LMDESC ; i++) lmdesc[i].header =0 ;
@@ -677,7 +674,9 @@ static void rz_dmac_irq_handle_channel(sif (chstat &CHSTAT_ER ) {dev_err (dmac->dev,"DMAC err CHSTAT_%d = %08X\n" , channel->index, chstat); -rz_dmac_ch_writel (channel,CHCTRL_DEFAULT ,CHCTRL ,1 ); + + scoped_guard(spinlock_irqsave, &channel->vc.lock) +rz_dmac_ch_writel (channel,CHCTRL_DEFAULT ,CHCTRL ,1 ); goto done; }
Greg Kroah-Hartman
maintainerPATCH 172/244 drm/amdgpu: prevent immediate PASID reuse case
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Huang <[email protected]>
commit 14b81abe7bdc25f8097906fc2f91276ffedb2d26 upstream.
PASID resue could cause interrupt issue when process immediately runs into hw state left by previous process exited with the same PASID, it's possible that page faults are still pending in the IH ring buffer when the process exits and frees up its PASID. To prevent the case, it uses idr cyclic allocator same as kernel pid's.
--- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c | 45 ++++++++++++++++++++++---------- drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h | 1 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 1 3 files changed, 34 insertions(+), 13 deletions(-)
drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +32 -13
@@ -35,10 +35,13 @@* PASIDs are global address space identifiers that can be shared * between the GPU, an IOMMU and the driver. VMs on different devices * may use the same PASID if they share the same address -* space. Therefore PASIDs are allocated using a global IDA. VMs are -* looked up from the PASID per amdgpu_device. +* space. Therefore PASIDs are allocated using IDR cyclic allocator +* (similar to kernel PID allocation) which naturally delays reuse. +* VMs are looked up from the PASID per amdgpu_device. */ -static DEFINE_IDA (amdgpu_pasid_ida); + +static DEFINE_IDR (amdgpu_pasid_idr); +static DEFINE_SPINLOCK (amdgpu_pasid_idr_lock);/* Helper to free pasid from a fence callback */ struct amdgpu_pasid_cb {
@@ -50,8 +53,8 @@ struct amdgpu_pasid_cb {* amdgpu_pasid_alloc - Allocate a PASID * @bits: Maximum width of the PASID in bits, must be at least 1 * -* Allocates a PASID of the given width while keeping smaller PASIDs -* available if possible. +* Uses kernel's IDR cyclic allocator (same as PID allocation). +* Allocates sequentially with automatic wrap-around. * * Returns a positive integer on success. Returns %-EINVAL if bits==0. * Returns %-ENOSPC if no PASID was available. Returns %-ENOMEM on
@@ -59,14 +62,15 @@ struct amdgpu_pasid_cb {*/ int amdgpu_pasid_alloc (unsigned int bits) { -int pasid = -EINVAL ; +int pasid; -for (bits =min (bits,31U ); bits >0 ; bits--) { - pasid =ida_alloc_range (&amdgpu_pasid_ida,1U << (bits -1 ), - (1U << bits) -1 ,GFP_KERNEL ); -if (pasid != -ENOSPC ) -break ; - } +if (bits ==0 ) +return -EINVAL ; + +spin_lock (&amdgpu_pasid_idr_lock); + pasid =idr_alloc_cyclic (&amdgpu_pasid_idr,NULL ,1 , +1U << bits,GFP_KERNEL ); +spin_unlock (&amdgpu_pasid_idr_lock);if (pasid >=0 )trace_amdgpu_pasid_allocated (pasid);
@@ -81,7 +85,10 @@ int amdgpu_pasid_alloc(unsigned int bitsvoid amdgpu_pasid_free (u32 pasid) {trace_amdgpu_pasid_freed (pasid); -ida_free (&amdgpu_pasid_ida, pasid); + +spin_lock (&amdgpu_pasid_idr_lock); +idr_remove (&amdgpu_pasid_idr, pasid); +spin_unlock (&amdgpu_pasid_idr_lock); } static void amdgpu_pasid_free_cb(struct dma_fence *fence,
@@ -635,3 +642,15 @@ void amdgpu_vmid_mgr_fini(struct amdgpu_ } } } + +/** +* amdgpu_pasid_mgr_cleanup - cleanup PASID manager +* +* Cleanup the IDR allocator. +*/ +void amdgpu_pasid_mgr_cleanup (void ) +{ +spin_lock (&amdgpu_pasid_idr_lock); +idr_destroy (&amdgpu_pasid_idr); +spin_unlock (&amdgpu_pasid_idr_lock); +}
drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h +1 -0
@@ -75,6 +75,7 @@ int amdgpu_pasid_alloc(unsigned int bitsvoid amdgpu_pasid_free (u32 pasid);void amdgpu_pasid_free_delayed (struct dma_resv *resv,u32 pasid); +void amdgpu_pasid_mgr_cleanup (void );bool amdgpu_vmid_had_gpu_reset (struct amdgpu_device *adev,struct amdgpu_vmid *id);
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +1 -0
@@ -2721,6 +2721,7 @@ void amdgpu_vm_manager_fini(struct amdgpxa_destroy (&adev->vm_manager.pasids);amdgpu_vmid_mgr_fini (adev); +amdgpu_pasid_mgr_cleanup (); }/**
Greg Kroah-Hartman
maintainerPATCH 173/244 drm/i915/dp_tunnel: Fix error handling when clearing stream BW in atomic state
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Imre Deak <[email protected]>
commit 77fcf58df15edcf3f5b5421f24814fb72796def9 upstream.
Clearing the DP tunnel stream BW in the atomic state involves getting the tunnel group state, which can fail. Handle the error accordingly.
This fixes at least one issue where drm_dp_tunnel_atomic_set_stream_bw() failed to get the tunnel group state returning -EDEADLK, which wasn't handled. This lead to the ctx->contended warn later in modeset_lock() while taking a WW mutex for another object in the same atomic state, and thus within the same already contended WW context.
Moving intel_crtc_state_alloc() later would avoid freeing saved_state on the error path; this stable patch leaves that simplification for a follow-up.
--- drivers/gpu/drm/i915/display/intel_display.c | 8 +++++++- drivers/gpu/drm/i915/display/intel_dp_tunnel.c | 20 ++++++++++++++------ drivers/gpu/drm/i915/display/intel_dp_tunnel.h | 11 +++++++---- 3 files changed, 28 insertions(+), 11 deletions(-)
drivers/gpu/drm/i915/display/intel_display.c +7 -1
@@ -4588,6 +4588,7 @@ intel_crtc_prepare_cleared_state(structintel_atomic_get_new_crtc_state (state, crtc);struct drm_i915_private *dev_priv =to_i915 (crtc->base.dev);struct intel_crtc_state *saved_state; +int err; saved_state =intel_crtc_state_alloc (crtc);if (!saved_state)
@@ -4596,7 +4597,12 @@ intel_crtc_prepare_cleared_state(struct/* free the old crtc_state->hw members */ intel_crtc_free_hw_state (crtc_state); -intel_dp_tunnel_atomic_clear_stream_bw (state, crtc_state); + err =intel_dp_tunnel_atomic_clear_stream_bw (state, crtc_state); +if (err) { +kfree (saved_state); + +return err; + }/* FIXME: before the switch to atomic started, a new pipe_config was * kzalloc'd. Code that depends on any field being zero should be
drivers/gpu/drm/i915/display/intel_dp_tunnel.c +14 -6
@@ -622,19 +622,27 @@ int intel_dp_tunnel_atomic_compute_strea* * Clear any DP tunnel stream BW requirement set by * intel_dp_tunnel_atomic_compute_stream_bw(). +* +* Returns 0 in case of success, a negative error code otherwise. */ -void intel_dp_tunnel_atomic_clear_stream_bw (struct intel_atomic_state *state, -struct intel_crtc_state *crtc_state) +int intel_dp_tunnel_atomic_clear_stream_bw (struct intel_atomic_state *state, +struct intel_crtc_state *crtc_state) {struct intel_crtc *crtc =to_intel_crtc (crtc_state->uapi.crtc); +int err;if (!crtc_state->dp_tunnel_ref.tunnel) -return ; +return 0 ; + + err =drm_dp_tunnel_atomic_set_stream_bw (&state->base, + crtc_state->dp_tunnel_ref.tunnel, + crtc->pipe,0 ); +if (err) +return err; -drm_dp_tunnel_atomic_set_stream_bw (&state->base, - crtc_state->dp_tunnel_ref.tunnel, - crtc->pipe,0 );drm_dp_tunnel_ref_put (&crtc_state->dp_tunnel_ref); + +return 0 ; }/**
drivers/gpu/drm/i915/display/intel_dp_tunnel.h +7 -4
@@ -39,8 +39,8 @@ int intel_dp_tunnel_atomic_compute_streastruct intel_dp *intel_dp, const struct intel_connector *connector, struct intel_crtc_state *crtc_state); -void intel_dp_tunnel_atomic_clear_stream_bw (struct intel_atomic_state *state, -struct intel_crtc_state *crtc_state); +int intel_dp_tunnel_atomic_clear_stream_bw (struct intel_atomic_state *state, +struct intel_crtc_state *crtc_state);int intel_dp_tunnel_atomic_add_state_for_crtc (struct intel_atomic_state *state,struct intel_crtc *crtc);
@@ -87,9 +87,12 @@ intel_dp_tunnel_atomic_compute_stream_bwreturn 0 ; } -static inline void +static inline int intel_dp_tunnel_atomic_clear_stream_bw (struct intel_atomic_state *state, -struct intel_crtc_state *crtc_state) {} +struct intel_crtc_state *crtc_state) +{ +return 0 ; +} static inline int intel_dp_tunnel_atomic_add_state_for_crtc(struct intel_atomic_state *state,
Greg Kroah-Hartman
maintainerPATCH 174/244 LoongArch: Fix missing NULL checks for kstrdup()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Jun <[email protected]>
commit 3a28daa9b7d7c2ddf2c722e9e95d7e0928bf0cd1 upstream.
1. Replace "of_find_node_by_path("/")" with "of_root" to avoid multiple calls to "of_node_put()".
2. Fix a potential kernel oops during early boot when memory allocation fails while parsing CPU model from device tree.
--- arch/loongarch/kernel/env.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
arch/loongarch/kernel/env.c +3 -4
@@ -42,16 +42,15 @@ static int __init init_cpu_fullname(voidint cpu, ret;char *cpuname;const char *model; -struct device_node *root;/* Parsing cpuname from DTS model property */ - root =of_find_node_by_path ("/" ); - ret =of_property_read_string (root,"model" , &model); + ret =of_property_read_string (of_root,"model" , &model);if (ret ==0 ) { cpuname =kstrdup (model,GFP_KERNEL ); +if (!cpuname) +return -ENOMEM ; loongson_sysconf.cpuname =strsep (&cpuname," " ); } -of_node_put (root);if (loongson_sysconf.cpuname && !strncmp (loongson_sysconf.cpuname,"Loongson" ,8 )) {for (cpu =0 ; cpu <NR_CPUS ; cpu++)
Greg Kroah-Hartman
maintainerPATCH 175/244 LoongArch: Workaround LS2K/LS7A GPU DMA hang bug
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huacai Chen <[email protected]>
commit 95db0c9f526d583634cddb2e5914718570fbac87 upstream.
1. Hardware limitation: GPU, DC and VPU are typically PCI device 06.0, 06.1 and 06.2. They share some hardware resources, so when configure the PCI 06.0 device BAR1, DMA memory access cannot be performed through this BAR, otherwise it will cause hardware abnormalities.
2. In typical scenarios of reboot or S3/S4, DC access to memory through BAR is not prohibited, resulting in GPU DMA hangs.
3. Workaround method: When configuring the 06.0 device BAR1, turn off the memory access of DC, GPU and VPU (via DC's CRTC registers).
--- arch/loongarch/pci/pci.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+)
arch/loongarch/pci/pci.c +80 -0
@@ -6,9 +6,11 @@#include <linux/export.h> #include <linux/init.h> #include <linux/acpi.h> +#include <linux/delay.h> #include <linux/types.h> #include <linux/pci.h> #include <linux/vgaarb.h> +#include <linux/io-64-nonatomic-lo-hi.h> #include <asm/cacheflush.h> #include <asm/loongson.h>
@@ -16,6 +18,9 @@#define PCI_DEVICE_ID_LOONGSON_DC1 0x7a06#define PCI_DEVICE_ID_LOONGSON_DC2 0x7a36#define PCI_DEVICE_ID_LOONGSON_DC3 0x7a46 +#define PCI_DEVICE_ID_LOONGSON_GPU1 0x7a15 +#define PCI_DEVICE_ID_LOONGSON_GPU2 0x7a25 +#define PCI_DEVICE_ID_LOONGSON_GPU3 0x7a35int raw_pci_read (unsigned int domain,unsigned int bus,unsigned int devfn,int reg,int len,u32 *val)
@@ -100,3 +105,78 @@ static void pci_fixup_vgadev(struct pci_DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_LOONGSON ,PCI_DEVICE_ID_LOONGSON_DC1 , pci_fixup_vgadev);DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_LOONGSON ,PCI_DEVICE_ID_LOONGSON_DC2 , pci_fixup_vgadev);DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_LOONGSON ,PCI_DEVICE_ID_LOONGSON_DC3 , pci_fixup_vgadev); + +#define CRTC_NUM_MAX 2 +#define CRTC_OUTPUT_ENABLE 0x100 + +static void loongson_gpu_fixup_dma_hang (struct pci_dev *pdev,bool on) +{ +u32 i, val, count, crtc_offset, device; +void __iomem *crtc_reg, *base, *regbase; +static u32 crtc_status[CRTC_NUM_MAX ] = {0 }; + + base = pdev->bus->ops->map_bus (pdev->bus, pdev->devfn +1 ,0 ); + device =readw (base +PCI_DEVICE_ID ); + + regbase =ioremap (readq (base +PCI_BASE_ADDRESS_0 ) & ~0xffull ,SZ_64K ); +if (!regbase) { +pci_err (pdev,"Failed to ioremap()\n" ); +return ; + } + +switch (device) { +case PCI_DEVICE_ID_LOONGSON_DC2 : + crtc_reg = regbase +0x1240 ; + crtc_offset =0x10 ; +break ; +case PCI_DEVICE_ID_LOONGSON_DC3 : + crtc_reg = regbase; + crtc_offset =0x400 ; +break ; + } + +for (i =0 ; i <CRTC_NUM_MAX ; i++, crtc_reg += crtc_offset) { + val =readl (crtc_reg); + +if (!on) + crtc_status[i] = val; + +/* No need to fixup if the status is off at startup. */ +if (!(crtc_status[i] &CRTC_OUTPUT_ENABLE )) +continue ; + +if (on) + val |=CRTC_OUTPUT_ENABLE ; +else + val &= ~CRTC_OUTPUT_ENABLE ; + +mb (); +writel (val, crtc_reg); + +for (count =0 ; count <40 ; count++) { + val =readl (crtc_reg) &CRTC_OUTPUT_ENABLE ; +if ((on && val) || (!on && !val)) +break ; +udelay (1000 ); + } + +pci_info (pdev,"DMA hang fixup at reg[0x%lx]: 0x%x\n" , + (unsigned long )crtc_reg &0xffff ,readl (crtc_reg)); + } + +iounmap (regbase); +} + +static void pci_fixup_dma_hang_early (struct pci_dev *pdev) +{ +loongson_gpu_fixup_dma_hang (pdev, false); +} +DECLARE_PCI_FIXUP_EARLY (PCI_VENDOR_ID_LOONGSON ,PCI_DEVICE_ID_LOONGSON_GPU2 , pci_fixup_dma_hang_early); +DECLARE_PCI_FIXUP_EARLY (PCI_VENDOR_ID_LOONGSON ,PCI_DEVICE_ID_LOONGSON_GPU3 , pci_fixup_dma_hang_early); + +static void pci_fixup_dma_hang_final (struct pci_dev *pdev) +{ +loongson_gpu_fixup_dma_hang (pdev, true); +} +DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_LOONGSON ,PCI_DEVICE_ID_LOONGSON_GPU2 , pci_fixup_dma_hang_final); +DECLARE_PCI_FIXUP_FINAL (PCI_VENDOR_ID_LOONGSON ,PCI_DEVICE_ID_LOONGSON_GPU3 , pci_fixup_dma_hang_final);
Greg Kroah-Hartman
maintainerPATCH 176/244 LoongArch: KVM: Make kvm_get_vcpu_by_cpuid() more robust
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huacai Chen <[email protected]>
commit 2db06c15d8c7a0ccb6108524e16cd9163753f354 upstream.
kvm_get_vcpu_by_cpuid() takes a cpuid parameter whose type is int, so cpuid can be negative. Let kvm_get_vcpu_by_cpuid() return NULL for this case so as to make it more robust.
This fix an out-of-bounds access to kvm_arch::phyid_map::phys_map[].
--- arch/loongarch/kvm/vcpu.c | 3 +++ 1 file changed, 3 insertions(+)
arch/loongarch/kvm/vcpu.c +3 -0
@@ -531,6 +531,9 @@ struct kvm_vcpu *kvm_get_vcpu_by_cpuid(s {struct kvm_phyid_map *map; +if (cpuid <0 ) +return NULL ; +if (cpuid >=KVM_MAX_PHYID )return NULL ;
Greg Kroah-Hartman
maintainerPATCH 177/244 xfs: stop reclaim before pushing AIL during unmount
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuto Ohnuki <[email protected]>
commit 4f24a767e3d64a5f58c595b5c29b6063a201f1e3 upstream.
The unmount sequence in xfs_unmount_flush_inodes() pushed the AIL while background reclaim and inodegc are still running. This is broken independently of any use-after-free issues - background reclaim and inodegc should not be running while the AIL is being pushed during unmount, as inodegc can dirty and insert inodes into the AIL during the flush, and background reclaim can race to abort and free dirty inodes.
Reorder xfs_unmount_flush_inodes() to stop inodegc and cancel background reclaim before pushing the AIL. Stop inodegc before cancelling m_reclaim_work because the inodegc worker can re-queue m_reclaim_work via xfs_inodegc_set_reclaimable.
--- fs/xfs/xfs_mount.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
fs/xfs/xfs_mount.c +4 -3
@@ -588,8 +588,9 @@ xfs_unmount_check(* have been retrying in the background. This will prevent never-ending * retries in AIL pushing from hanging the unmount. * -* Finally, we can push the AIL to clean all the remaining dirty objects, then -* reclaim the remaining inodes that are still in memory at this point in time. +* Stop inodegc and background reclaim before pushing the AIL so that they +* are not running while the AIL is being flushed. Then push the AIL to +* clean all the remaining dirty objects and reclaim the remaining inodes. */ static void xfs_unmount_flush_inodes(
@@ -601,9 +602,9 @@ xfs_unmount_flush_inodes(xfs_set_unmounting (mp); -xfs_ail_push_all_sync (mp->m_ail);xfs_inodegc_stop (mp);cancel_delayed_work_sync (&mp->m_reclaim_work); +xfs_ail_push_all_sync (mp->m_ail);xfs_reclaim_inodes (mp);xfs_health_unmount (mp); }
Greg Kroah-Hartman
maintainerPATCH 178/244 xfs: save ailp before dropping the AIL lock in push callbacks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuto Ohnuki <[email protected]>
commit 394d70b86fae9fe865e7e6d9540b7696f73aa9b6 upstream.
In xfs_inode_item_push() and xfs_qm_dquot_logitem_push(), the AIL lock is dropped to perform buffer IO. Once the cluster buffer no longer protects the log item from reclaim, the log item may be freed by background reclaim or the dquot shrinker. The subsequent spin_lock() call dereferences lip->li_ailp, which is a use-after-free.
Fix this by saving the ailp pointer in a local variable while the AIL lock is held and the log item is guaranteed to be valid.
--- fs/xfs/xfs_dquot_item.c | 9 +++++++-- fs/xfs/xfs_inode_item.c | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-)
fs/xfs/xfs_dquot_item.c +7 -2
@@ -126,6 +126,7 @@ xfs_qm_dquot_logitem_push(struct xfs_dq_logitem *qlip =DQUOT_ITEM (lip);struct xfs_dquot *dqp = qlip->qli_dquot;struct xfs_buf *bp; +struct xfs_ail *ailp = lip->li_ailp;uint rval =XFS_ITEM_SUCCESS ;int error;
@@ -154,7 +155,7 @@ xfs_qm_dquot_logitem_push( goto out_unlock; } -spin_unlock (&lip->li_ailp->ail_lock); +spin_unlock (&ailp->ail_lock); error =xfs_dquot_use_attached_buf (dqp, &bp);if (error == -EAGAIN ) {
@@ -173,9 +174,13 @@ xfs_qm_dquot_logitem_push( rval =XFS_ITEM_FLUSHING ; }xfs_buf_relse (bp); +/* +* The buffer no longer protects the log item from reclaim, so +* do not reference lip after this point. +*/ out_relock_ail: -spin_lock (&lip->li_ailp->ail_lock); +spin_lock (&ailp->ail_lock); out_unlock:xfs_dqunlock (dqp);return rval;
fs/xfs/xfs_inode_item.c +7 -2
@@ -726,6 +726,7 @@ xfs_inode_item_push(struct xfs_inode_log_item *iip =INODE_ITEM (lip);struct xfs_inode *ip = iip->ili_inode;struct xfs_buf *bp = lip->li_buf; +struct xfs_ail *ailp = lip->li_ailp;uint rval =XFS_ITEM_SUCCESS ;int error;
@@ -748,7 +749,7 @@ xfs_inode_item_push(if (!xfs_buf_trylock (bp))return XFS_ITEM_LOCKED ; -spin_unlock (&lip->li_ailp->ail_lock); +spin_unlock (&ailp->ail_lock);/* * We need to hold a reference for flushing the cluster buffer as it may
@@ -772,7 +773,11 @@ xfs_inode_item_push( rval =XFS_ITEM_LOCKED ; } -spin_lock (&lip->li_ailp->ail_lock); +/* +* The buffer no longer protects the log item from reclaim, so +* do not reference lip after this point. +*/ +spin_lock (&ailp->ail_lock);return rval; }
Greg Kroah-Hartman
maintainerPATCH 179/244 xfs: scrub: unlock dquot before early return in quota scrub
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: hongao <[email protected]>
commit 268378b6ad20569af0d1957992de1c8b16c6e900 upstream.
xchk_quota_item can return early after calling xchk_fblock_process_error. When that helper returns false, the function returned immediately without dropping dq->q_qlock, which can leave the dquot lock held and risk lock leaks or deadlocks in later quota operations.
Fix this by unlocking dq->q_qlock before the early return.
--- fs/xfs/scrub/quota.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/xfs/scrub/quota.c +3 -1
@@ -174,8 +174,10 @@ xchk_quota_item( error =xchk_quota_item_bmap (sc, dq, offset);xchk_iunlock (sc,XFS_ILOCK_SHARED ); -if (!xchk_fblock_process_error (sc,XFS_DATA_FORK , offset, &error)) +if (!xchk_fblock_process_error (sc,XFS_DATA_FORK , offset, &error)) { +mutex_unlock (&dq->q_qlock);return error; + }/* * Warn if the hard limits are larger than the fs.
Greg Kroah-Hartman
maintainerPATCH 180/244 xfs: fix ri_total validation in xlog_recover_attri_commit_pass2
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Long Li <[email protected]>
commit d72f2084e30966097c8eae762e31986a33c3c0ae upstream.
The ri_total checks for SET/REPLACE operations are hardcoded to 3, but xfs_attri_item_size() only emits a value iovec when value_len > 0, so ri_total is 2 when value_len == 0.
For PPTR_SET/PPTR_REMOVE/PPTR_REPLACE, value_len is validated by xfs_attri_validate() to be exactly sizeof(struct xfs_parent_rec) and is never zero, so their hardcoded checks remain correct.
This problem may cause log recovery failures. The following script can be used to reproduce the problem:
#!/bin/bash
mkfs.xfs -f /dev/sda
mount /dev/sda /mnt/test/
touch /mnt/test/file
for i in {1..200}; do
attr -s "user.attr_$i" -V "value_$i" /mnt/test/file > /dev/null
done
echo 1 > /sys/fs/xfs/debug/larp
echo 1 > /sys/fs/xfs/sda/errortag/larp
attr -s "user.zero" -V "" /mnt/test/file
echo 0 > /sys/fs/xfs/sda/errortag/larp
umount /mnt/test
mount /dev/sda /mnt/test/ # mount failed
Fix this by deriving the expected count dynamically as "2 + !!value_len" for SET/REPLACE operations.
--- fs/xfs/xfs_attr_item.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/xfs/xfs_attr_item.c +2 -2
@@ -1052,8 +1052,8 @@ xlog_recover_attri_commit_pass2(break ;case XFS_ATTRI_OP_FLAGS_SET :case XFS_ATTRI_OP_FLAGS_REPLACE : -/* Log item, attr name, attr value */ -if (item->ri_total !=3 ) { +/* Log item, attr name, optional attr value */ +if (item->ri_total !=2 + !!attri_formatp->alfi_value_len) {XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, attri_formatp, len);return -EFSCORRUPTED ;
Greg Kroah-Hartman
maintainerPATCH 181/244 xfs: dont irele after failing to iget in xfs_attri_recover_work
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Darrick J. Wong <[email protected]>
commit 70685c291ef82269180758130394ecdc4496b52c upstream.
xlog_recovery_iget* never set @ip to a valid pointer if they return an error, so this irele will walk off a dangling pointer. Fix that.
--- fs/xfs/xfs_attr_item.c | 1 - 1 file changed, 1 deletion(-)
fs/xfs/xfs_attr_item.c +0 -1
@@ -658,7 +658,6 @@ xfs_attri_recover_work(break ; }if (error) { -xfs_irele (ip);XFS_CORRUPTION_ERROR (__func__,XFS_ERRLEVEL_LOW , mp, attrp,sizeof (*attrp));return ERR_PTR (-EFSCORRUPTED );
Greg Kroah-Hartman
maintainerPATCH 182/244 xfs: remove file_path tracepoint data
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Darrick J. Wong <[email protected]>
commit e31c53a8060e134111ed095783fee0aa0c43b080 upstream.
The xfile/xmbuf shmem file descriptions are no longer as detailed as they were when online fsck was first merged, because moving to static strings in commit 60382993a2e180 ("xfs: get rid of the xchk_xfile_*_descr calls") removed a memory allocation and hence a source of failure.
However this makes encoding the description in the tracepoints sort of a waste of memory. David Laight also points out that file_path doesn't zero the whole buffer which causes exposure of stale trace bytes, and Steven Rostedt wonders why we're not using a dynamic array for the file path.
I don't think this is worth fixing, so let's just rip it out.
--- fs/xfs/scrub/trace.h | 12 ++---------- fs/xfs/xfs_trace.h | 11 ++--------- 2 files changed, 4 insertions(+), 19 deletions(-)
fs/xfs/scrub/trace.h +2 -10
@@ -959,20 +959,12 @@ TRACE_EVENT(xfile_create,TP_STRUCT__entry (__field (dev_t ,dev ) __field(unsigned long, ino) -__array (char, pathname,MAXNAMELEN ) ),TP_fast_assign ( - char *path; - __entry->ino = file_inode(xf->file)->i_ino; - path =file_path (xf->file, __entry->pathname,MAXNAMELEN ); -if (IS_ERR (path)) -strncpy (__entry->pathname,"(unknown)" , -sizeof (__entry->pathname)); ), -TP_printk ("xfino 0x%lx path '%s'" , - __entry->ino, - __entry->pathname) +TP_printk ("xfino 0x%lx" , + __entry->ino) ); TRACE_EVENT(xfile_destroy,
fs/xfs/xfs_trace.h +2 -9
@@ -4710,23 +4710,16 @@ TRACE_EVENT(xmbuf_create,TP_STRUCT__entry (__field (dev_t ,dev ) __field(unsigned long, ino) -__array (char, pathname,MAXNAMELEN ) ),TP_fast_assign ( - char *path; struct file *file = btp->bt_file; __entry->dev = btp->bt_mount->m_super->s_dev; __entry->ino =file_inode (file)->i_ino; - path =file_path (file, __entry->pathname,MAXNAMELEN ); -if (IS_ERR (path)) -strncpy (__entry->pathname,"(unknown)" , -sizeof (__entry->pathname)); ), -TP_printk ("dev %d:%d xmino 0x%lx path '%s'" , +TP_printk ("dev %d:%d xmino 0x%lx" ,MAJOR (__entry->dev),MINOR (__entry->dev), - __entry->ino, - __entry->pathname) + __entry->ino) ); TRACE_EVENT(xmbuf_free,
Greg Kroah-Hartman
maintainerPATCH 183/244 ext4: fix journal credit check when setting fscrypt context
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Simon Weber <[email protected]>
commit b1d682f1990c19fb1d5b97d13266210457092bcd upstream.
Fix an issue arising when ext4 features has_journal, ea_inode, and encrypt are activated simultaneously, leading to ENOSPC when creating an encrypted file.
Fix by passing XATTR_CREATE flag to xattr_set_handle function if a handle is specified, i.e., when the function is called in the control flow of creating a new inode. This aligns the number of jbd2 credits set_handle checks for with the number allocated for creating a new inode.
ext4_set_context must not be called with a non-null handle (fs_data) if fscrypt context xattr is not guaranteed to not exist yet. The only other usage of this function currently is when handling the ioctl FS_IOC_SET_ENCRYPTION_POLICY, which calls it with fs_data=NULL.
--- fs/ext4/crypto.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
fs/ext4/crypto.c +8 -1
@@ -163,10 +163,17 @@ static int ext4_set_context(struct inode*/ if (handle) { +/* +* Since the inode is new it is ok to pass the +* XATTR_CREATE flag. This is necessary to match the +* remaining journal credits check in the set_handle +* function with the credits allocated for the new +* inode. +*/ res =ext4_xattr_set_handle (handle, inode,EXT4_XATTR_INDEX_ENCRYPTION ,EXT4_XATTR_NAME_ENCRYPTION_CONTEXT , - ctx, len,0 ); + ctx, len,XATTR_CREATE );if (!res) {ext4_set_inode_flag (inode,EXT4_INODE_ENCRYPT ); ext4_clear_inode_state(inode,
Greg Kroah-Hartman
maintainerPATCH 184/244 ext4: convert inline data to extents when truncate exceeds inline size
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
commit ed9356a30e59c7cc3198e7fc46cfedf3767b9b17 upstream.
Add a check in ext4_setattr() to convert files from inline data storage to extent-based storage when truncate() grows the file size beyond the inline capacity. This prevents the filesystem from entering an inconsistent state where the inline data flag is set but the file size exceeds what can be stored inline.
Without this fix, the following sequence causes a kernel BUG_ON():
1. Mount filesystem with inode that has inline flag set and small size
2. truncate(file, 50MB) - grows size but inline flag remains set
3. sendfile() attempts to write data
4. ext4_write_inline_data() hits BUG_ON(write_size > inline_capacity)
The crash occurs because ext4_write_inline_data() expects inline storage to accommodate the write, but the actual inline capacity (~60 bytes for i_block + ~96 bytes for xattrs) is far smaller than the file size and write request.
The fix checks if the new size from setattr exceeds the inode's actual inline capacity (EXT4_I(inode)->i_inline_size) and converts the file to extent-based storage before proceeding with the size change.
This addresses the root cause by ensuring the inline data flag and file size remain consistent during truncate operations.
--- fs/ext4/inode.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
fs/ext4/inode.c +12 -0
@@ -5490,6 +5490,18 @@ int ext4_setattr(struct mnt_idmap *idmapif (attr->ia_size == inode->i_size) inc_ivers = false; +/* +* If file has inline data but new size exceeds inline capacity, +* convert to extent-based storage first to prevent inconsistent +* state (inline flag set but size exceeds inline capacity). +*/ +if (ext4_has_inline_data (inode) && + attr->ia_size >EXT4_I (inode)->i_inline_size) { + error =ext4_convert_inline_data (inode); +if (error) + goto err_out; + } +if (shrink) {if (ext4_should_order_data (inode)) { error = ext4_begin_ordered_truncate(inode,
Greg Kroah-Hartman
maintainerPATCH 185/244 ext4: fix stale xarray tags after writeback
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Kara <[email protected]>
commit f4a2b42e78914ff15630e71289adc589c3a8eb45 upstream.
There are cases where ext4_bio_write_page() gets called for a page which has no buffers to submit. This happens e.g. when the part of the file is actually a hole, when we cannot allocate blocks due to being called from jbd2, or in data=journal mode when checkpointing writes the buffers earlier. In these cases we just return from ext4_bio_write_page() however if the page didn't need redirtying, we will leave stale DIRTY and/or TOWRITE tags in xarray because those get cleared only in __folio_start_writeback(). As a result we can leave these tags set in mappings even after a final sync on filesystem that's getting remounted read-only or that's being frozen. Various assertions can then get upset when writeback is started on such filesystems (Gerald reported assertion in ext4_journal_check_start() firing).
Fix the problem by cycling the page through writeback state even if we decide nothing needs to be written for it so that xarray tags get properly updated. This is slightly silly (we could update the xarray tags directly) but I don't think a special helper messing with xarray tags is really worth it in this relatively rare corner case.
--- fs/ext4/page-io.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
fs/ext4/page-io.c +8 -2
@@ -506,9 +506,15 @@ int ext4_bio_write_folio(struct ext4_io_ nr_to_submit++; }while ((bh = bh->b_this_page) != head); -/* Nothing to submit? Just unlock the folio... */ -if (!nr_to_submit) +if (!nr_to_submit) { +/* +* We have nothing to submit. Just cycle the folio through +* writeback state to properly update xarray tags. +*/ +__folio_start_writeback (folio, keep_towrite); +folio_end_writeback (folio);return 0 ; + } bh = head =folio_buffers (folio);
Greg Kroah-Hartman
maintainerPATCH 186/244 ext4: fix fsync(2) for nojournal mode
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Kara <[email protected]>
commit 1308255bbf8452762f89f44f7447ce137ecdbcff upstream.
When inode metadata is changed, we sometimes just call ext4_mark_inode_dirty() to track modified metadata. This copies inode metadata into block buffer which is enough when we are journalling metadata. However when we are running in nojournal mode we currently fail to write the dirtied inode buffer during fsync(2) because the inode is not marked as dirty. Use explicit ext4_write_inode() call to make sure the inode table buffer is written to the disk. This is a band aid solution but proper solution requires a much larger rewrite including changes in metadata bh tracking infrastructure.
--- fs/ext4/fsync.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-)
fs/ext4/fsync.c +14 -2
@@ -83,11 +83,23 @@ static int ext4_fsync_nojournal(struct f int datasync, bool *needs_barrier) { struct inode *inode = file->f_inode; +struct writeback_control wbc = { + .sync_mode =WB_SYNC_ALL , + .nr_to_write =0 , + };int ret; ret =generic_buffers_fsync_noflush (file, start, end, datasync); -if (!ret) - ret =ext4_sync_parent (inode); +if (ret) +return ret; + +/* Force writeout of inode table buffer to disk */ + ret =ext4_write_inode (inode, &wbc); +if (ret) +return ret; + + ret =ext4_sync_parent (inode); +if (test_opt (inode->i_sb,BARRIER )) *needs_barrier = true;
Greg Kroah-Hartman
maintainerPATCH 187/244 ext4: make recently_deleted() properly work with lazy itable initialization
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jan Kara <[email protected]>
commit bd060afa7cc3e0ad30afa9ecc544a78638498555 upstream.
recently_deleted() checks whether inode has been used in the near past. However this can give false positive result when inode table is not initialized yet and we are in fact comparing to random garbage (or stale itable block of a filesystem before mkfs). Ultimately this results in uninitialized inodes being skipped during inode allocation and possibly they are never initialized and thus e2fsck complains. Verify if the inode has been initialized before checking for dtime.
--- fs/ext4/ialloc.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/ext4/ialloc.c +6 -0
@@ -686,6 +686,12 @@ static int recently_deleted(struct superif (unlikely (!gdp))return 0 ; +/* Inode was never used in this filesystem? */ +if (ext4_has_group_desc_csum (sb) && + (gdp->bg_flags &cpu_to_le16 (EXT4_BG_INODE_UNINIT ) || + ino >=EXT4_INODES_PER_GROUP (sb) -ext4_itable_unused_count (sb, gdp))) +return 0 ; + bh =sb_find_get_block (sb,ext4_inode_table (sb, gdp) + (ino / inodes_per_block));if (!bh || !buffer_uptodate (bh))
Greg Kroah-Hartman
maintainerPATCH 188/244 ext4: replace BUG_ON with proper error handling in ext4_read_inline_folio
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuto Ohnuki <[email protected]>
commit 356227096eb66e41b23caf7045e6304877322edf upstream.
Replace BUG_ON() with proper error handling when inline data size exceeds PAGE_SIZE. This prevents kernel panic and allows the system to continue running while properly reporting the filesystem corruption.
The error is logged via ext4_error_inode(), the buffer head is released to prevent memory leak, and -EFSCORRUPTED is returned to indicate filesystem corruption.
--- fs/ext4/inline.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
fs/ext4/inline.c +9 -1
@@ -517,7 +517,15 @@ static int ext4_read_inline_folio(struct goto out; len =min_t (size_t,ext4_get_inline_size (inode),i_size_read (inode)); -BUG_ON (len >PAGE_SIZE ); + +if (len >PAGE_SIZE ) { +ext4_error_inode (inode, __func__, __LINE__,0 , +"inline size %zu exceeds PAGE_SIZE" , len); + ret = -EFSCORRUPTED ; +brelse (iloc.bh); + goto out; + } + kaddr =kmap_local_folio (folio,0 ); ret =ext4_read_inline_data (inode, kaddr, len, &iloc); kaddr =folio_zero_tail (folio, len, kaddr + len);
Greg Kroah-Hartman
maintainerPATCH 189/244 ext4: validate p_idx bounds in ext4_ext_correct_indexes
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tejas Bharambe <[email protected]>
commit 2acb5c12ebd860f30e4faf67e6cc8c44ddfe5fe8 upstream.
ext4_ext_correct_indexes() walks up the extent tree correcting index entries when the first extent in a leaf is modified. Before accessing path[k].p_idx->ei_block, there is no validation that p_idx falls within the valid range of index entries for that level.
If the on-disk extent header contains a corrupted or crafted eh_entries value, p_idx can point past the end of the allocated buffer, causing a slab-out-of-bounds read.
Fix this by validating path[k].p_idx against EXT_LAST_INDEX() at both access sites: before the while loop and inside it. Return -EFSCORRUPTED if the index pointer is out of range, consistent with how other bounds violations are handled in the ext4 extent tree code.
--- fs/ext4/extents.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
fs/ext4/extents.c +15 -0
@@ -1739,6 +1739,13 @@ static int ext4_ext_correct_indexes(hand err =ext4_ext_get_access (handle, inode, path + k);if (err)return err; +if (unlikely (path[k].p_idx >EXT_LAST_INDEX (path[k].p_hdr))) { +EXT4_ERROR_INODE (inode, +"path[%d].p_idx %p > EXT_LAST_INDEX %p" , + k, path[k].p_idx, +EXT_LAST_INDEX (path[k].p_hdr)); +return -EFSCORRUPTED ; + } path[k].p_idx->ei_block = border; err =ext4_ext_dirty (handle, inode, path + k);if (err)
@@ -1751,6 +1758,14 @@ static int ext4_ext_correct_indexes(hand err =ext4_ext_get_access (handle, inode, path + k);if (err) goto clean; +if (unlikely (path[k].p_idx >EXT_LAST_INDEX (path[k].p_hdr))) { +EXT4_ERROR_INODE (inode, +"path[%d].p_idx %p > EXT_LAST_INDEX %p" , + k, path[k].p_idx, +EXT_LAST_INDEX (path[k].p_hdr)); + err = -EFSCORRUPTED ; + goto clean; + } path[k].p_idx->ei_block = border; err =ext4_ext_dirty (handle, inode, path + k);if (err)
Greg Kroah-Hartman
maintainerPATCH 190/244 ext4: avoid infinite loops caused by residual data
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Edward Adam Davis <[email protected]>
commit 5422fe71d26d42af6c454ca9527faaad4e677d6c upstream.
On the mkdir/mknod path, when mapping logical blocks to physical blocks, if inserting a new extent into the extent tree fails (in this example, because the file system disabled the huge file feature when marking the inode as dirty), ext4_ext_map_blocks() only calls ext4_free_blocks() to reclaim the physical block without deleting the corresponding data in the extent tree. This causes subsequent mkdir operations to reference the previously reclaimed physical block number again, even though this physical block is already being used by the xattr block. Therefore, a situation arises where both the directory and xattr are using the same buffer head block in memory simultaneously.
The above causes ext4_xattr_block_set() to enter an infinite loop about "inserted" and cannot release the inode lock, ultimately leading to the 143s blocking problem mentioned in [1].
If the metadata is corrupted, then trying to remove some extent space can do even more harm. Also in case EXT4_GET_BLOCKS_DELALLOC_RESERVE was passed, remove space wrongly update quota information. Jan Kara suggests distinguishing between two cases:
1) The error is ENOSPC or EDQUOT - in this case the filesystem is fully consistent and we must maintain its consistency including all the accounting. However these errors can happen only early before we've inserted the extent into the extent tree. So current code works correctly for this case.
2) Some other error - this means metadata is corrupted. We should strive to do as few modifications as possible to limit damage. So I'd just skip freeing of allocated blocks.
[1]
INFO: task syz.0.17:5995 blocked for more than 143 seconds. Call Trace: inode_lock_nested include/linux/fs.h:1073 [inline] __start_dirop fs/namei.c:2923 [inline] start_dirop fs/namei.c:2934 [inline]
--- fs/ext4/extents.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
fs/ext4/extents.c +6 -2
@@ -4458,9 +4458,13 @@ got_allocated_blocks: path =ext4_ext_insert_extent (handle, inode, path, &newex, flags);if (IS_ERR (path)) { err =PTR_ERR (path); -if (allocated_clusters) { +/* +* Gracefully handle out of space conditions. If the filesystem +* is inconsistent, we'll just leak allocated blocks to avoid +* causing even more damage. +*/ +if (allocated_clusters && (err == -EDQUOT || err == -ENOSPC )) {int fb_flags =0 ; -/* * free data blocks we just allocated. * not a good idea to call discard here directly,
Greg Kroah-Hartman
maintainerPATCH 191/244 ext4: avoid allocate block from corrupted group in ext4_mb_find_by_goal()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ye Bin <[email protected]>
commit 46066e3a06647c5b186cc6334409722622d05c44 upstream.
There's issue as follows:
...
EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 206 at logical offset 0 with max blocks 1 with error 117 EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 206 at logical offset 0 with max blocks 1 with error 117 EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 206 at logical offset 0 with max blocks 1 with error 117 EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 206 at logical offset 0 with max blocks 1 with error 117 EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 2243 at logical offset 0 with max blocks 1 with error 117 EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
EXT4-fs (mmcblk0p1): Delayed block allocation failed for inode 2239 at logical offset 0 with max blocks 1 with error 117 EXT4-fs (mmcblk0p1): This should not happen!! Data will be lost
EXT4-fs (mmcblk0p1): error count since last fsck: 1
EXT4-fs (mmcblk0p1): initial error at time 1765597433: ext4_mb_generate_buddy:760 EXT4-fs (mmcblk0p1): last error at time 1765597433: ext4_mb_generate_buddy:760
...
According to the log analysis, blocks are always requested from the corrupted block group. This may happen as follows:ext4_mb_find_by_goal
ext4_mb_load_buddy
ext4_mb_load_buddy_gfp
ext4_mb_init_cache
ext4_read_block_bitmap_nowait
ext4_wait_block_bitmap
ext4_validate_block_bitmap
if (!grp || EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
return -EFSCORRUPTED; // There's no logs.
if (err)
return err; // Will return errorext4_lock_group(ac->ac_sb, group);
if (unlikely(EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info))) // Unreachable
goto out;
After commit 9008a58e5dce ("ext4: make the bitmap read routines return real error codes") merged, Commit 163a203ddb36 ("ext4: mark block group as corrupt on block bitmap error") is no real solution for allocating blocks from corrupted block groups. This is because if 'EXT4_MB_GRP_BBITMAP_CORRUPT(e4b->bd_info)' is true, then 'ext4_mb_load_buddy()' may return an error. This means that the block allocation will fail.
Therefore, check block group if corrupted when ext4_mb_load_buddy() returns error.
--- fs/ext4/mballoc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
fs/ext4/mballoc.c +5 -1
@@ -2440,8 +2440,12 @@ int ext4_mb_find_by_goal(struct ext4_allreturn 0 ; err =ext4_mb_load_buddy (ac->ac_sb, group, e4b); -if (err) +if (err) { +if (EXT4_MB_GRP_BBITMAP_CORRUPT (e4b->bd_info) && + !(ac->ac_flags &EXT4_MB_HINT_GOAL_ONLY )) +return 0 ;return err; + }ext4_lock_group (ac->ac_sb, group);if (unlikely (EXT4_MB_GRP_BBITMAP_CORRUPT (e4b->bd_info)))
Greg Kroah-Hartman
maintainerPATCH 192/244 ext4: reject mount if bigalloc with s_first_data_block != 0
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Helen Koike <[email protected]>
commit 3822743dc20386d9897e999dbb990befa3a5b3f8 upstream.
bigalloc with s_first_data_block != 0 is not supported, reject mounting it.
--- fs/ext4/super.c | 7 +++++++ 1 file changed, 7 insertions(+)
fs/ext4/super.c +7 -0
@@ -3631,6 +3631,13 @@ int ext4_feature_set_ok(struct super_blo "extents feature\n");return 0 ; } +if (ext4_has_feature_bigalloc (sb) && +le32_to_cpu (EXT4_SB (sb)->s_es->s_first_data_block)) { +ext4_msg (sb,KERN_WARNING , +"bad geometry: bigalloc file system with non-zero " +"first_data_block\n" ); +return 0 ; + }#if !IS_ENABLED (CONFIG_QUOTA ) || !IS_ENABLED (CONFIG_QFMT_V2 ) if (!readonly && (ext4_has_feature_quota(sb) ||
Greg Kroah-Hartman
maintainerPATCH 193/244 ext4: fix use-after-free in update_super_work when racing with umount
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jiayuan Chen <[email protected]>
commit d15e4b0a418537aafa56b2cb80d44add83e83697 upstream.
Commit b98535d09179 ("ext4: fix bug_on in start_this_handle during umount filesystem") moved ext4_unregister_sysfs() before flushing s_sb_upd_work to prevent new error work from being queued via /proc/fs/ext4/xx/mb_groups reads during unmount. However, this introduced a use-after-free because update_super_work calls ext4_notify_error_sysfs() -> sysfs_notify() which accesses the kobject's kernfs_node after it has been freed by kobject_del() in ext4_unregister_sysfs():
update_super_work ext4_put_super
----------------- --------------
ext4_unregister_sysfs(sb)
kobject_del(&sbi->s_kobj)
__kobject_del()
sysfs_remove_dir()
kobj->sd = NULL
sysfs_put(sd)
kernfs_put() // RCU free
ext4_notify_error_sysfs(sbi)
sysfs_notify(&sbi->s_kobj)
kn = kobj->sd // stale pointer
kernfs_get(kn) // UAF on freed kernfs_node
ext4_journal_destroy()
flush_work(&sbi->s_sb_upd_work)Instead of reordering the teardown sequence, fix this by making ext4_notify_error_sysfs() detect that sysfs has already been torn down by checking s_kobj.state_in_sysfs, and skipping the sysfs_notify() call in that case. A dedicated mutex (s_error_notify_mutex) serializes ext4_notify_error_sysfs() against kobject_del() in ext4_unregister_sysfs() to prevent TOCTOU races where the kobject could be deleted between the state_in_sysfs check and the sysfs_notify() call.
--- fs/ext4/ext4.h | 1 + fs/ext4/super.c | 1 + fs/ext4/sysfs.c | 10 +++++++++- 3 files changed, 11 insertions(+), 1 deletion(-)
fs/ext4/ext4.h +1 -0
@@ -1534,6 +1534,7 @@ struct ext4_sb_info {struct proc_dir_entry *s_proc;struct kobject s_kobj;struct completion s_kobj_unregister; +struct mutex s_error_notify_mutex;/* protects sysfs_notify vs kobject_del */ struct super_block *s_sb;struct buffer_head *s_mmp_bh;
fs/ext4/super.c +1 -0
@@ -5313,6 +5313,7 @@ static int __ext4_fill_super(struct fs_ctimer_setup (&sbi->s_err_report, print_daily_error_info,0 );spin_lock_init (&sbi->s_error_lock); +mutex_init (&sbi->s_error_notify_mutex);INIT_WORK (&sbi->s_sb_upd_work, update_super_work); err =ext4_group_desc_init (sb, es, logical_sb_block, &first_not_zeroed);
fs/ext4/sysfs.c +9 -1
@@ -551,7 +551,10 @@ static const struct kobj_type ext4_feat_void ext4_notify_error_sysfs (struct ext4_sb_info *sbi) { -sysfs_notify (&sbi->s_kobj,NULL ,"errors_count" ); +mutex_lock (&sbi->s_error_notify_mutex); +if (sbi->s_kobj.state_in_sysfs) +sysfs_notify (&sbi->s_kobj,NULL ,"errors_count" ); +mutex_unlock (&sbi->s_error_notify_mutex); }static struct kobject *ext4_root;
@@ -564,8 +567,10 @@ int ext4_register_sysfs(struct super_bloint err;init_completion (&sbi->s_kobj_unregister); +mutex_lock (&sbi->s_error_notify_mutex); err =kobject_init_and_add (&sbi->s_kobj, &ext4_sb_ktype, ext4_root,"%s" , sb->s_id); +mutex_unlock (&sbi->s_error_notify_mutex);if (err) {kobject_put (&sbi->s_kobj);wait_for_completion (&sbi->s_kobj_unregister);
@@ -598,7 +603,10 @@ void ext4_unregister_sysfs(struct super_if (sbi->s_proc)remove_proc_subtree (sb->s_id, ext4_proc_root); + +mutex_lock (&sbi->s_error_notify_mutex);kobject_del (&sbi->s_kobj); +mutex_unlock (&sbi->s_error_notify_mutex); }int __init ext4_init_sysfs (void )
Greg Kroah-Hartman
maintainerPATCH 194/244 ext4: fix the might_sleep() warnings in kvfree()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zqiang <[email protected]>
commit 496bb99b7e66f48b178126626f47e9ba79e2d0fa upstream.
Use the kvfree() in the RCU read critical section can trigger the following warnings:
EXT4-fs (vdb): unmounting filesystem cd983e5b-3c83-4f5a-a136-17b00eb9d018.
WARNING: suspicious RCU usage
./include/linux/rcupdate.h:409 Illegal context switch in RCU read-side critical section!
other info that might help us debug this:
rcu_scheduler_active = 2, debug_locks = 1
Call Trace: <TASK> dump_stack_lvl+0xbb/0xd0 dump_stack+0x14/0x20 lockdep_rcu_suspicious+0x15a/0x1b0 __might_resched+0x375/0x4d0 ? put_object.part.0+0x2c/0x50 __might_sleep+0x108/0x160 vfree+0x58/0x910 ? ext4_group_desc_free+0x27/0x270 kvfree+0x23/0x40 ext4_group_desc_free+0x111/0x270 ext4_put_super+0x3c8/0xd40 generic_shutdown_super+0x14c/0x4a0 ? __pfx_shrinker_free+0x10/0x10 kill_block_super+0x40/0x90 ext4_kill_sb+0x6d/0xb0 deactivate_locked_super+0xb4/0x180 deactivate_super+0x7e/0xa0 cleanup_mnt+0x296/0x3e0 __cleanup_mnt+0x16/0x20 task_work_run+0x157/0x250 ? __pfx_task_work_run+0x10/0x10 ? exit_to_user_mode_loop+0x6a/0x550 exit_to_user_mode_loop+0x102/0x550 do_syscall_64+0x44a/0x500 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> BUG: sleeping function called from invalid context at mm/vmalloc.c:3441 in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 556, name: umount preempt_count: 1, expected: 0 CPU: 3 UID: 0 PID: 556 Comm: umount Call Trace: <TASK> dump_stack_lvl+0xbb/0xd0 dump_stack+0x14/0x20 __might_resched+0x275/0x4d0 ? put_object.part.0+0x2c/0x50 __might_sleep+0x108/0x160 vfree+0x58/0x910 ? ext4_group_desc_free+0x27/0x270 kvfree+0x23/0x40 ext4_group_desc_free+0x111/0x270 ext4_put_super+0x3c8/0xd40 generic_shutdown_super+0x14c/0x4a0 ? __pfx_shrinker_free+0x10/0x10 kill_block_super+0x40/0x90 ext4_kill_sb+0x6d/0xb0 deactivate_locked_super+0xb4/0x180 deactivate_super+0x7e/0xa0 cleanup_mnt+0x296/0x3e0 __cleanup_mnt+0x16/0x20 task_work_run+0x157/0x250 ? __pfx_task_work_run+0x10/0x10 ? exit_to_user_mode_loop+0x6a/0x550 exit_to_user_mode_loop+0x102/0x550 do_syscall_64+0x44a/0x500 entry_SYSCALL_64_after_hwframe+0x77/0x7f
The above scenarios occur in initialization failures and teardown paths, there are no parallel operations on the resources released by kvfree(), this commit therefore remove rcu_read_lock/unlock() and use rcu_access_pointer() instead of rcu_dereference() operations.
--- fs/ext4/mballoc.c | 10 +++------- fs/ext4/super.c | 8 ++------ 2 files changed, 5 insertions(+), 13 deletions(-)
fs/ext4/mballoc.c +3 -7
@@ -3575,9 +3575,7 @@ err_freebuddy:rcu_read_unlock ();iput (sbi->s_buddy_cache); err_freesgi: -rcu_read_lock (); -kvfree (rcu_dereference (sbi->s_group_info)); -rcu_read_unlock (); +kvfree (rcu_access_pointer (sbi->s_group_info));return -ENOMEM ; }
@@ -3882,7 +3880,8 @@ void ext4_mb_release(struct super_blockWARN_ON_ONCE (!list_empty (&sbi->s_discard_list)); } -if (sbi->s_group_info) { + group_info =rcu_access_pointer (sbi->s_group_info); +if (group_info) {for (i =0 ; i < ngroups; i++) {cond_resched (); grinfo =ext4_get_group_info (sb, i);
@@ -3900,12 +3899,9 @@ void ext4_mb_release(struct super_block num_meta_group_infos = (ngroups +EXT4_DESC_PER_BLOCK (sb) -1 ) >>EXT4_DESC_PER_BLOCK_BITS (sb); -rcu_read_lock (); - group_info =rcu_dereference (sbi->s_group_info);for (i =0 ; i < num_meta_group_infos; i++)kfree (group_info[i]);kvfree (group_info); -rcu_read_unlock (); }ext4_mb_avg_fragment_size_destroy (sbi);ext4_mb_largest_free_orders_destroy (sbi);
fs/ext4/super.c +2 -6
@@ -1270,12 +1270,10 @@ static void ext4_group_desc_free(structstruct buffer_head **group_desc;int i; -rcu_read_lock (); - group_desc =rcu_dereference (sbi->s_group_desc); + group_desc =rcu_access_pointer (sbi->s_group_desc);for (i =0 ; i < sbi->s_gdb_count; i++)brelse (group_desc[i]);kvfree (group_desc); -rcu_read_unlock (); }static void ext4_flex_groups_free (struct ext4_sb_info *sbi)
@@ -1283,14 +1281,12 @@ static void ext4_flex_groups_free(structstruct flex_groups **flex_groups;int i; -rcu_read_lock (); - flex_groups =rcu_dereference (sbi->s_flex_groups); + flex_groups =rcu_access_pointer (sbi->s_flex_groups);if (flex_groups) {for (i =0 ; i < sbi->s_flex_groups_allocated; i++)kvfree (flex_groups[i]);kvfree (flex_groups); } -rcu_read_unlock (); }static void ext4_put_super (struct super_block *sb)
Greg Kroah-Hartman
maintainerPATCH 195/244 ext4: handle wraparound when searching for blocks for indirect mapped blocks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Theodore Ts'o <[email protected]>
commit bb81702370fad22c06ca12b6e1648754dbc37e0f upstream.
Commit 4865c768b563 ("ext4: always allocate blocks only from groups inode can use") restricts what blocks will be allocated for indirect block based files to block numbers that fit within 32-bit block numbers.
However, when using a review bot running on the latest Gemini LLM to check this commit when backporting into an LTS based kernel, it raised this concern:
If ac->ac_g_ex.fe_group is >= ngroups (for instance, if the goal group was populated via stream allocation from s_mb_last_groups), then start will be >= ngroups.
Does this allow allocating blocks beyond the 32-bit limit for indirect block mapped files? The commit message mentions that ext4_mb_scan_groups_linear() takes care to not select unsupported groups. However, its loop uses group = *start, and the very first iteration will call ext4_mb_scan_group() with this unsupported group because next_linear_group() is only called at the end of the iteration.
After reviewing the code paths involved and considering the LLM review, I determined that this can happen when there is a file system where some files/directories are extent-mapped and others are indirect-block mapped. To address this, add a safety clamp in ext4_mb_scan_groups().
--- fs/ext4/mballoc.c | 2 ++ 1 file changed, 2 insertions(+)
fs/ext4/mballoc.c +2 -0
@@ -1199,6 +1199,8 @@ static int ext4_mb_scan_groups(struct ex/* searching for the right group start from the goal value specified */ start = ac->ac_g_ex.fe_group; +if (start >= ngroups) + start =0 ; ac->ac_prefetch_grp = start; ac->ac_prefetch_nr =0 ;
Greg Kroah-Hartman
maintainerPATCH 196/244 ext4: fix iloc.bh leak in ext4_fc_replay_inode() error paths
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Baokun Li <[email protected]>
commit ec0a7500d8eace5b4f305fa0c594dd148f0e8d29 upstream.
During code review, Joseph found that ext4_fc_replay_inode() calls ext4_get_fc_inode_loc() to get the inode location, which holds a reference to iloc.bh that must be released via brelse().
However, several error paths jump to the 'out' label without releasing iloc.bh:
- ext4_handle_dirty_metadata() failure
- sync_dirty_buffer() failure
- ext4_mark_inode_used() failure
- ext4_iget() failure
Fix this by introducing an 'out_brelse' label placed just before the existing 'out' label to ensure iloc.bh is always released.
Additionally, make ext4_fc_replay_inode() propagate errors properly instead of always returning 0.
--- fs/ext4/fast_commit.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
fs/ext4/fast_commit.c +8 -5
@@ -1601,19 +1601,21 @@ static int ext4_fc_replay_inode(struct s/* Immediately update the inode on disk. */ ret =ext4_handle_dirty_metadata (NULL ,NULL , iloc.bh);if (ret) - goto out; + goto out_brelse; ret =sync_dirty_buffer (iloc.bh);if (ret) - goto out; + goto out_brelse; ret =ext4_mark_inode_used (sb, ino);if (ret) - goto out; + goto out_brelse;/* Given that we just wrote the inode on disk, this SHOULD succeed. */ inode =ext4_iget (sb, ino,EXT4_IGET_NORMAL );if (IS_ERR (inode)) {ext4_debug ("Inode not found." ); -return -EFSCORRUPTED ; + inode =NULL ; + ret = -EFSCORRUPTED ; + goto out_brelse; }/*
@@ -1630,13 +1632,14 @@ static int ext4_fc_replay_inode(struct sext4_inode_csum_set (inode,ext4_raw_inode (&iloc),EXT4_I (inode)); ret =ext4_handle_dirty_metadata (NULL ,NULL , iloc.bh);sync_dirty_buffer (iloc.bh); +out_brelse:brelse (iloc.bh); out:iput (inode);if (!ret)blkdev_issue_flush (sb->s_bdev); -return 0 ; +return ret; }/*
Greg Kroah-Hartman
maintainerPATCH 197/244 ext4: always drain queued discard work in ext4_mb_release()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Theodore Ts'o <[email protected]>
commit 9ee29d20aab228adfb02ca93f87fb53c56c2f3af upstream.
While reviewing recent ext4 patch[1], Sashiko raised the following concern[2]:
Quoted 5 lines
If the filesystem is initially mounted with the discard option, deleting files will populate
sbi->s_discard_listand queues_discard_work. If it is then remounted with nodiscard, theEXT4_MOUNT_DISCARDflag is cleared, but the pendings_discard_workis neither cancelled nor flushed.
[1] https://lore.kernel.org/r/[email protected]/
[2] https://sashiko.dev/#/patchset/20260319094545.19291-1-qiang.zhang%40linux.dev
The concern was valid, but it had nothing to do with the patch[1]. One of the problems with Sashiko in its current (early) form is that it will detect pre-existing issues and report it as a problem with the patch that it is reviewing.
In practice, it would be hard to hit deliberately (unless you are a malicious syzkaller fuzzer), since it would involve mounting the file system with -o discard, and then deleting a large number of files, remounting the file system with -o nodiscard, and then immediately unmounting the file system before the queued discard work has a change to drain on its own.
Fix it because it's a real bug, and to avoid Sashiko from raising this concern when analyzing future patches to mballoc.c.
--- fs/ext4/mballoc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
fs/ext4/mballoc.c +5 -7
@@ -3874,13 +3874,11 @@ void ext4_mb_release(struct super_blockstruct kmem_cache *cachep =get_groupinfo_cache (sb->s_blocksize_bits);int count; -if (test_opt (sb,DISCARD )) { -/* -* wait the discard work to drain all of ext4_free_data -*/ -flush_work (&sbi->s_discard_work); -WARN_ON_ONCE (!list_empty (&sbi->s_discard_list)); - } +/* +* wait the discard work to drain all of ext4_free_data +*/ +flush_work (&sbi->s_discard_work); +WARN_ON_ONCE (!list_empty (&sbi->s_discard_list)); group_info =rcu_access_pointer (sbi->s_group_info);if (group_info) {
Greg Kroah-Hartman
maintainerPATCH 198/244 arm64: dts: imx8mn-tqma8mqnl: fix LDO5 power off
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Markus Niebel <[email protected]>
commit 8adc841d43ebceabec996c9dcff6e82d3e585268 upstream.
Fix SD card removal caused by automatic LDO5 power off after boot
To prevent this, add vqmmc regulator for USDHC, using a GPIO-controlled regulator that is supplied by LDO5. Since this is implemented on SoM but used on baseboards with SD-card interface, implement the functionality on SoM part and optionally enable it on baseboards if needed.
--- arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx.dts | 13 ++++---- arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi | 22 ++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-)
arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl-mba8mx.dts +7 -6
@@ -64,6 +64,10 @@ }; }; +®_usdhc2_vqmmc { + status = "okay"; +}; + &sai3 { assigned-clocks = <&clk IMX8MN_CLK_SAI3>; assigned-clock-parents = <&clk IMX8MN_AUDIO_PLL1_OUT>;
@@ -207,8 +211,7 @@ <MX8MN_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4>, <MX8MN_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4>, <MX8MN_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4>, - <MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4>, - <MX8MN_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x84>; + <MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4>; }; pinctrl_usdhc2_100mhz: usdhc2-100mhzgrp {
@@ -217,8 +220,7 @@ <MX8MN_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4>, <MX8MN_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4>, <MX8MN_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4>, - <MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4>, - <MX8MN_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x84>; + <MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4>; }; pinctrl_usdhc2_200mhz: usdhc2-200mhzgrp {
@@ -227,8 +229,7 @@ <MX8MN_IOMUXC_SD2_DATA0_USDHC2_DATA0 0x1d4>, <MX8MN_IOMUXC_SD2_DATA1_USDHC2_DATA1 0x1d4>, <MX8MN_IOMUXC_SD2_DATA2_USDHC2_DATA2 0x1d4>, - <MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4>, - <MX8MN_IOMUXC_GPIO1_IO04_USDHC2_VSELECT 0x84>; + <MX8MN_IOMUXC_SD2_DATA3_USDHC2_DATA3 0x1d4>; }; pinctrl_usdhc2_gpio: usdhc2-gpiogrp {
arch/arm64/boot/dts/freescale/imx8mn-tqma8mqnl.dtsi +22 -0
@@ -30,6 +30,20 @@ regulator-max-microvolt = <3300000>; }; + reg_usdhc2_vqmmc: regulator-usdhc2-vqmmc { + compatible = "regulator-gpio"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_reg_usdhc2_vqmmc>; + regulator-name = "V_SD2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; + states = <1800000 0x1>, + <3300000 0x0>; + vin-supply = <&ldo5_reg>; + status = "disabled"; + }; + reserved-memory { #address-cells = <2>; #size-cells = <2>;
@@ -228,6 +242,10 @@ vddio-supply = <&ldo3_reg>; }; +&usdhc2 { + vqmmc-supply = <®_usdhc2_vqmmc>; +}; + &usdhc3 { pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc3>;
@@ -282,6 +300,10 @@ fsl,pins = <MX8MN_IOMUXC_SD2_RESET_B_GPIO2_IO19 0x84>; }; + pinctrl_reg_usdhc2_vqmmc: regusdhc2vqmmcgrp { + fsl,pins = <MX8MN_IOMUXC_GPIO1_IO04_GPIO1_IO4 0xc0>; + }; + pinctrl_usdhc3: usdhc3grp { fsl,pins = <MX8MN_IOMUXC_NAND_WE_B_USDHC3_CLK 0x1d4>, <MX8MN_IOMUXC_NAND_WP_B_USDHC3_CMD 0x1d2>,
Greg Kroah-Hartman
maintainerPATCH 199/244 powerpc64/bpf: do not increment tailcall count when prog is NULL
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hari Bathini <[email protected]>
commit 521bd39d9d28ce54cbfec7f9b89c94ad4fdb8350 upstream.
Do not increment tailcall count, if tailcall did not succeed due to missing BPF program.
[ Conflict due to missing feature commit 2ed2d8f6fb38 ("powerpc64/bpf:
Support tailcalls with subprogs") resolved accordingly. ]
--- arch/powerpc/net/bpf_jit_comp64.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-)
arch/powerpc/net/bpf_jit_comp64.c +14 -9
@@ -404,27 +404,32 @@ static int bpf_jit_emit_tail_call(u32 *i/* * tail_call_cnt++; +* Writeback this updated value only if tailcall succeeds. */ EMIT (PPC_RAW_ADDI (bpf_to_ppc (TMP_REG_1 ),bpf_to_ppc (TMP_REG_1 ),1 )); -EMIT (PPC_RAW_STD (bpf_to_ppc (TMP_REG_1 ), _R1,bpf_jit_stack_tailcallcnt (ctx)));/* prog = array->ptrs[index]; */ -EMIT (PPC_RAW_MULI (bpf_to_ppc (TMP_REG_1 ), b2p_index,8 )); -EMIT (PPC_RAW_ADD (bpf_to_ppc (TMP_REG_1 ),bpf_to_ppc (TMP_REG_1 ), b2p_bpf_array)); -EMIT (PPC_RAW_LD (bpf_to_ppc (TMP_REG_1 ),bpf_to_ppc (TMP_REG_1 ), offsetof(struct bpf_array , ptrs))); +EMIT (PPC_RAW_MULI (bpf_to_ppc (TMP_REG_2 ), b2p_index,8 )); +EMIT (PPC_RAW_ADD (bpf_to_ppc (TMP_REG_2 ),bpf_to_ppc (TMP_REG_2 ), b2p_bpf_array)); +EMIT (PPC_RAW_LD (bpf_to_ppc (TMP_REG_2 ),bpf_to_ppc (TMP_REG_2 ), + offsetof(struct bpf_array , ptrs)));/* * if (prog == NULL) * goto out; */ -EMIT (PPC_RAW_CMPLDI (bpf_to_ppc (TMP_REG_1 ),0 )); +EMIT (PPC_RAW_CMPLDI (bpf_to_ppc (TMP_REG_2 ),0 ));PPC_BCC_SHORT (COND_EQ , out);/* goto *(prog->bpf_func + prologue_size); */ -EMIT (PPC_RAW_LD (bpf_to_ppc (TMP_REG_1 ),bpf_to_ppc (TMP_REG_1 ), offsetof(struct bpf_prog , bpf_func))); -EMIT (PPC_RAW_ADDI (bpf_to_ppc (TMP_REG_1 ),bpf_to_ppc (TMP_REG_1 ), -FUNCTION_DESCR_SIZE + bpf_tailcall_prologue_size)); -EMIT (PPC_RAW_MTCTR (bpf_to_ppc (TMP_REG_1 ))); +EMIT (PPC_RAW_LD (bpf_to_ppc (TMP_REG_2 ),bpf_to_ppc (TMP_REG_2 ), + offsetof(struct bpf_prog , bpf_func))); +EMIT (PPC_RAW_ADDI (bpf_to_ppc (TMP_REG_2 ),bpf_to_ppc (TMP_REG_2 ), +FUNCTION_DESCR_SIZE + bpf_tailcall_prologue_size)); +EMIT (PPC_RAW_MTCTR (bpf_to_ppc (TMP_REG_2 ))); + +/* Writeback updated tailcall count */ +EMIT (PPC_RAW_STD (bpf_to_ppc (TMP_REG_1 ), _R1,bpf_jit_stack_tailcallcnt (ctx)));/* tear down stack, restore NVRs, ... */ bpf_jit_emit_common_epilogue (image, ctx);
Greg Kroah-Hartman
maintainerPATCH 200/244 ksmbd: fix use-after-free and NULL deref in smb_grant_oplock()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Werner Kasselman <[email protected]>
[ Upstream commit 48623ec358c1c600fa1e38368746f933e0f1a617 ]
smb_grant_oplock() has two issues in the oplock publication sequence:
1) opinfo is linked into ci->m_op_list (via opinfo_add) before add_lease_global_list() is called. If add_lease_global_list() fails (kmalloc returns NULL), the error path frees the opinfo via __free_opinfo() while it is still linked in ci->m_op_list. Concurrent m_op_list readers (opinfo_get_list, or direct iteration in smb_break_all_levII_oplock) dereference the freed node.
2) opinfo->o_fp is assigned after add_lease_global_list() publishes the opinfo on the global lease list. A concurrent find_same_lease_key() can walk the lease list and dereference opinfo->o_fp->f_ci while o_fp is still NULL.
Fix by restructuring the publication sequence to eliminate post-publish failure:
- Set opinfo->o_fp before any list publication (fixes NULL deref).
- Preallocate lease_table via alloc_lease_table() before opinfo_add() so add_lease_global_list() becomes infallible after publication.
- Keep the original m_op_list publication order (opinfo_add before lease list) so concurrent opens via same_client_has_lease() and opinfo_get_list() still see the in-flight grant.
- Use opinfo_put() instead of __free_opinfo() on err_out so that the RCU-deferred free path is used.
This also requires splitting add_lease_global_list() to take a preallocated lease_table and changing its return type from int to void, since it can no longer fail.
--- fs/smb/server/oplock.c | 72 ++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 27 deletions(-)
fs/smb/server/oplock.c +45 -27
@@ -82,11 +82,19 @@ static void lease_del_list(struct oplockspin_unlock (&lb->lb_lock); } -static void lb_add (struct lease_table *lb) +static struct lease_table *alloc_lease_table (struct oplock_info *opinfo) { -write_lock (&lease_list_lock); -list_add (&lb->l_entry, &lease_table_list); -write_unlock (&lease_list_lock); +struct lease_table *lb; + + lb =kmalloc (sizeof (struct lease_table ),KSMBD_DEFAULT_GFP ); +if (!lb) +return NULL ; + +memcpy (lb->client_guid, opinfo->conn->ClientGUID, +SMB2_CLIENT_GUID_SIZE ); +INIT_LIST_HEAD (&lb->lease_list); +spin_lock_init (&lb->lb_lock); +return lb; }static int alloc_lease (struct oplock_info *opinfo,struct lease_ctx_info *lctx)
@@ -1042,34 +1050,27 @@ static void copy_lease(struct oplock_inf lease2->version = lease1->version; } -static int add_lease_global_list (struct oplock_info *opinfo) +static void add_lease_global_list (struct oplock_info *opinfo, +struct lease_table *new_lb) {struct lease_table *lb; -read_lock (&lease_list_lock); +write_lock (&lease_list_lock);list_for_each_entry (lb, &lease_table_list, l_entry) {if (!memcmp (lb->client_guid, opinfo->conn->ClientGUID,SMB2_CLIENT_GUID_SIZE )) { opinfo->o_lease->l_lb = lb;lease_add_list (opinfo); -read_unlock (&lease_list_lock); -return 0 ; +write_unlock (&lease_list_lock); +kfree (new_lb); +return ; } } -read_unlock (&lease_list_lock); - lb =kmalloc (sizeof (struct lease_table ),KSMBD_DEFAULT_GFP ); -if (!lb) -return -ENOMEM ; - -memcpy (lb->client_guid, opinfo->conn->ClientGUID, -SMB2_CLIENT_GUID_SIZE ); -INIT_LIST_HEAD (&lb->lease_list); -spin_lock_init (&lb->lb_lock); - opinfo->o_lease->l_lb = lb; + opinfo->o_lease->l_lb = new_lb;lease_add_list (opinfo); -lb_add (lb); -return 0 ; +list_add (&new_lb->l_entry, &lease_table_list); +write_unlock (&lease_list_lock); } static void set_oplock_level(struct oplock_info *opinfo, int level,
@@ -1189,6 +1190,7 @@ int smb_grant_oplock(struct ksmbd_work *int err =0 ;struct oplock_info *opinfo =NULL , *prev_opinfo =NULL ;struct ksmbd_inode *ci = fp->f_ci; +struct lease_table *new_lb =NULL ;bool prev_op_has_lease;__le32 prev_op_state =0 ;
@@ -1291,21 +1293,37 @@ set_lev:set_oplock_level (opinfo, req_op_level, lctx); out: -opinfo_count_inc (fp); -opinfo_add (opinfo, fp); - +/* +* Set o_fp before any publication so that concurrent readers +* (e.g. find_same_lease_key() on the lease list) that +* dereference opinfo->o_fp don't hit a NULL pointer. +* +* Keep the original publication order so concurrent opens can +* still observe the in-flight grant via ci->m_op_list, but make +* everything after opinfo_add() no-fail by preallocating any new +* lease_table first. +*/ + opinfo->o_fp = fp;if (opinfo->is_lease) { - err =add_lease_global_list (opinfo); -if (err) + new_lb =alloc_lease_table (opinfo); +if (!new_lb) { + err = -ENOMEM ; goto err_out; + } } +opinfo_count_inc (fp); +opinfo_add (opinfo, fp); + +if (opinfo->is_lease) +add_lease_global_list (opinfo, new_lb); +rcu_assign_pointer (fp->f_opinfo, opinfo); - opinfo->o_fp = fp;return 0 ; err_out: -__free_opinfo (opinfo); +kfree (new_lb); +opinfo_put (opinfo);return err; }
Greg Kroah-Hartman
maintainerPATCH 201/244 tracing: Switch trace_osnoise.c code over to use guard() and __free()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
[ Upstream commit 930d2b32c0af6895ba4c6ca6404e7f7b6dc214ed ]
The osnoise_hotplug_workfn() grabs two mutexes and cpu_read_lock(). It has various gotos to handle unlocking them. Switch them over to guard() and let the compiler worry about it.
The osnoise_cpus_read() has a temporary mask_str allocated and there's some gotos to make sure it gets freed on error paths. Switch that over to __free() to let the compiler worry about it.
Stable-dep-of: 1f9885732248 ("tracing: Fix potential deadlock in cpu hotplug with osnoise")
--- kernel/trace/trace_osnoise.c | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-)
kernel/trace/trace_osnoise.c +13 -27
@@ -2099,26 +2099,21 @@ static void osnoise_hotplug_workfn(struc {unsigned int cpu =smp_processor_id (); -mutex_lock (&trace_types_lock); +guard (mutex)(&trace_types_lock);if (!osnoise_has_registered_instances ()) - goto out_unlock_trace; +return ; -mutex_lock (&interface_lock); -cpus_read_lock (); +guard (mutex)(&interface_lock); +guard (cpus_read_lock)();if (!cpu_online (cpu)) - goto out_unlock; +return ; +if (!cpumask_test_cpu (cpu, &osnoise_cpumask)) - goto out_unlock; +return ;start_kthread (cpu); - -out_unlock: -cpus_read_unlock (); -mutex_unlock (&interface_lock); -out_unlock_trace: -mutex_unlock (&trace_types_lock); } static DECLARE_WORK(osnoise_hotplug_work, osnoise_hotplug_workfn);
@@ -2316,31 +2311,22 @@ static ssize_t osnoise_cpus_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) { - char *mask_str; + char *mask_str __free(kfree) = NULL;int len; -mutex_lock (&interface_lock); +guard (mutex)(&interface_lock); len =snprintf (NULL ,0 ,"%*pbl\n" ,cpumask_pr_args (&osnoise_cpumask)) +1 ; mask_str =kmalloc (len,GFP_KERNEL ); -if (!mask_str) { - count = -ENOMEM ; - goto out_unlock; - } +if (!mask_str) +return -ENOMEM ; len =snprintf (mask_str, len,"%*pbl\n" ,cpumask_pr_args (&osnoise_cpumask)); -if (len >= count) { - count = -EINVAL ; - goto out_free; - } +if (len >= count) +return -EINVAL ; count =simple_read_from_buffer (ubuf, count, ppos, mask_str, len); -out_free: -kfree (mask_str); -out_unlock: -mutex_unlock (&interface_lock); -return count; }
Greg Kroah-Hartman
maintainerPATCH 202/244 tracing: Fix potential deadlock in cpu hotplug with osnoise
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luo Haiyang <[email protected]>
[ Upstream commit 1f9885732248d22f788e4992c739a98c88ab8a55 ]
The following sequence may leads deadlock in cpu hotplug:
task1 task2 task3
----- ----- ----- mutex_lock(&interface_lock)
[CPU GOING OFFLINE]
cpus_write_lock();
osnoise_cpu_die();
kthread_stop(task3);
wait_for_completion();
osnoise_sleep();
mutex_lock(&interface_lock); cpus_read_lock();
[DEAD LOCK]
Fix by swap the order of cpus_read_lock() and mutex_lock(&interface_lock).
--- kernel/trace/trace_osnoise.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
kernel/trace/trace_osnoise.c +5 -5
@@ -2104,8 +2104,8 @@ static void osnoise_hotplug_workfn(strucif (!osnoise_has_registered_instances ())return ; -guard (mutex)(&interface_lock);guard (cpus_read_lock)(); +guard (mutex)(&interface_lock);if (!cpu_online (cpu))return ;
@@ -2268,11 +2268,11 @@ static ssize_t osnoise_options_write(strif (running)stop_per_cpu_kthreads (); -mutex_lock (&interface_lock);/* * avoid CPU hotplug operations that might read options. */ cpus_read_lock (); +mutex_lock (&interface_lock); retval = cnt;
@@ -2288,8 +2288,8 @@ static ssize_t osnoise_options_write(strclear_bit (option, &osnoise_options); } -cpus_read_unlock ();mutex_unlock (&interface_lock); +cpus_read_unlock ();if (running)start_per_cpu_kthreads ();
@@ -2375,16 +2375,16 @@ osnoise_cpus_write(struct file *filp, coif (running)stop_per_cpu_kthreads (); -mutex_lock (&interface_lock);/* * osnoise_cpumask is read by CPU hotplug operations. */ cpus_read_lock (); +mutex_lock (&interface_lock);cpumask_copy (&osnoise_cpumask, osnoise_cpumask_new); -cpus_read_unlock ();mutex_unlock (&interface_lock); +cpus_read_unlock ();if (running)start_per_cpu_kthreads ();
Greg Kroah-Hartman
maintainerPATCH 203/244 drm/xe: always keep track of remap prev/next
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthew Auld <[email protected]>
[ Upstream commit bfe9e314d7574d1c5c851972e7aee342733819d2 ]
During 3D workload, user is reporting hitting:
[ 413.361679] WARNING: drivers/gpu/drm/xe/xe_vm.c:1217 at vm_bind_ioctl_ops_unwind+0x1e2/0x2e0 [xe], CPU#7: vkd3d_queue/9925 [ 413.361944] CPU: 7 UID: 1000 PID: 9925 Comm: vkd3d_queue Kdump: loaded Not tainted 7.0.0-070000rc3-generic #202603090038 PREEMPT(lazy) [ 413.361949] RIP: 0010:vm_bind_ioctl_ops_unwind+0x1e2/0x2e0 [xe] [ 413.362074] RSP: 0018:ffffd4c25c3df930 EFLAGS: 00010282 [ 413.362077] RAX: 0000000000000000 RBX: ffff8f3ee817ed10 RCX: 0000000000000000 [ 413.362078] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 413.362079] RBP: ffffd4c25c3df980 R08: 0000000000000000 R09: 0000000000000000 [ 413.362081] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8f41fbf99380 [ 413.362082] R13: ffff8f3ee817e968 R14: 00000000ffffffef R15: ffff8f43d00bd380 [ 413.362083] FS: 00000001040ff6c0(0000) GS:ffff8f4696d89000(0000) knlGS:00000000330b0000 [ 413.362085] CS: 0010 DS: 002b ES: 002b CR0: 0000000080050033 [ 413.362086] CR2: 00007ddfc4747000 CR3: 00000002e6262005 CR4: 0000000000f72ef0 [ 413.362088] PKRU: 55555554 [ 413.362089] Call Trace: [ 413.362092] <TASK> [ 413.362096] xe_vm_bind_ioctl+0xa9a/0xc60 [xe]
Which seems to hint that the vma we are re-inserting for the ops unwind is either invalid or overlapping with something already inserted in the vm. It shouldn't be invalid since this is a re-insertion, so must have worked before. Leaving the likely culprit as something already placed where we want to insert the vma.
Following from that, for the case where we do something like a rebind in the middle of a vma, and one or both mapped ends are already compatible, we skip doing the rebind of those vma and set next/prev to NULL. As well as then adjust the original unmap va range, to avoid unmapping the ends. However, if we trigger the unwind path, we end up with three va, with the two ends never being removed and the original va range in the middle still being the shrunken size.
If this occurs, one failure mode is when another unwind op needs to interact with that range, which can happen with a vector of binds. For example, if we need to re-insert something in place of the original va. In this case the va is still the shrunken version, so when removing it and then doing a re-insert it can overlap with the ends, which were never removed, triggering a warning like above, plus leaving the vm in a bad state.
With that, we need two things here:
1) Stop nuking the prev/next tracking for the skip cases. Instead relying on checking for skip prev/next, where needed. That way on the unwind path, we now correctly remove both ends.
2) Undo the unmap va shrinkage, on the unwind path. With the two ends now removed the unmap va should expand back to the original size again, before re-insertion.
v2:
- Update the explanation in the commit message, based on an actual IGT of triggering this issue, rather than conjecture.
- Also undo the unmap shrinkage, for the skip case. With the two ends now removed, the original unmap va range should expand back to the original range.
v3:
- Track the old start/range separately. vma_size/start() uses the va info directly.
--- drivers/gpu/drm/xe/xe_pt.c | 12 ++++++------ drivers/gpu/drm/xe/xe_vm.c | 22 ++++++++++++++++++---- drivers/gpu/drm/xe/xe_vm_types.h | 4 ++++ 3 files changed, 28 insertions(+), 10 deletions(-)
drivers/gpu/drm/xe/xe_pt.c +6 -6
@@ -1281,9 +1281,9 @@ static int op_check_userptr(struct xe_vm err =vma_check_userptr (vm, op->map.vma, pt_update);break ;case DRM_GPUVA_OP_REMAP : -if (op->remap.prev) +if (op->remap.prev && !op->remap.skip_prev) err =vma_check_userptr (vm, op->remap.prev, pt_update); -if (!err && op->remap.next) +if (!err && op->remap.next && !op->remap.skip_next) err =vma_check_userptr (vm, op->remap.next, pt_update);break ;case DRM_GPUVA_OP_UNMAP :
@@ -1784,12 +1784,12 @@ static int op_prepare(struct xe_vm *vm, err =unbind_op_prepare (tile, pt_update_ops,gpuva_to_vma (op->base.remap.unmap->va)); -if (!err && op->remap.prev) { +if (!err && op->remap.prev && !op->remap.skip_prev) { err =bind_op_prepare (vm, tile, pt_update_ops, op->remap.prev); pt_update_ops->wait_vm_bookkeep = true; } -if (!err && op->remap.next) { +if (!err && op->remap.next && !op->remap.skip_next) { err =bind_op_prepare (vm, tile, pt_update_ops, op->remap.next); pt_update_ops->wait_vm_bookkeep = true;
@@ -1950,10 +1950,10 @@ static void op_commit(struct xe_vm *vm,gpuva_to_vma (op->base.remap.unmap->va), fence, fence2); -if (op->remap.prev) +if (op->remap.prev && !op->remap.skip_prev)bind_op_commit (vm, tile, pt_update_ops, op->remap.prev, fence, fence2); -if (op->remap.next) +if (op->remap.next && !op->remap.skip_next)bind_op_commit (vm, tile, pt_update_ops, op->remap.next, fence, fence2);break ;
drivers/gpu/drm/xe/xe_vm.c +18 -4
@@ -2187,7 +2187,6 @@ static int xe_vma_op_commit(struct xe_vmif (!err && op->remap.skip_prev) { op->remap.prev->tile_present = tile_present; - op->remap.prev =NULL ; } }if (op->remap.next) {
@@ -2197,11 +2196,13 @@ static int xe_vma_op_commit(struct xe_vmif (!err && op->remap.skip_next) { op->remap.next->tile_present = tile_present; - op->remap.next =NULL ; } } -/* Adjust for partial unbind after removin VMA from VM */ +/* +* Adjust for partial unbind after removing VMA from VM. In case +* of unwind we might need to undo this later. +*/ if (!err) { op->base.remap.unmap->va->va.addr = op->remap.start; op->base.remap.unmap->va->va.range = op->remap.range;
@@ -2273,6 +2274,8 @@ static int vm_bind_ioctl_ops_parse(struc op->remap.start =xe_vma_start (old); op->remap.range =xe_vma_size (old); + op->remap.old_start = op->remap.start; + op->remap.old_range = op->remap.range;if (op->base.remap.prev) { flags |= op->base.remap.unmap->va->flags &
@@ -2421,8 +2424,19 @@ static void xe_vma_op_unwind(struct xe_vdown_read (&vm->userptr.notifier_lock); vma->gpuva.flags &= ~XE_VMA_DESTROYED ;up_read (&vm->userptr.notifier_lock); -if (post_commit) +if (post_commit) { +/* +* Restore the old va range, in case of the +* prev/next skip optimisation. Otherwise what +* we re-insert here could be smaller than the +* original range. +*/ + op->base.remap.unmap->va->va.addr = + op->remap.old_start; + op->base.remap.unmap->va->va.range = + op->remap.old_range;xe_vm_insert_vma (vm, vma); + } }break ; }
drivers/gpu/drm/xe/xe_vm_types.h +4 -0
@@ -314,6 +314,10 @@ struct xe_vma_op_remap {u64 start;/** @range: range of the VMA unmap */ u64 range; +/** @old_start: Original start of the VMA we unmap */ +u64 old_start; +/** @old_range: Original range of the VMA we unmap */ +u64 old_range;/** @skip_prev: skip prev rebind */ bool skip_prev;/** @skip_next: skip next rebind */
Greg Kroah-Hartman
maintainerPATCH 204/244 LoongArch: vDSO: Emit GNU_EH_FRAME correctly
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xi Ruoyao <[email protected]>
commit e4878c37f6679fdea91b27a0f4e60a871f0b7bad upstream.
With -fno-asynchronous-unwind-tables and --no-eh-frame-hdr (the default of the linker), the GNU_EH_FRAME segment (specified by vdso.lds.S) is empty. This is not valid, as the current DWARF specification mandates the first byte of the EH frame to be the version number 1. It causes some unwinders to complain, for example the ClickHouse query profiler spams the log with messages:
clickhouse-server[365854]: libunwind: unsupported .eh_frame_hdr
version: 127 at 7ffffffb0000
Here "127" is just the byte located at the p_vaddr (0, i.e. the beginning of the vDSO) of the empty GNU_EH_FRAME segment. Cross- checking with /proc/365854/maps has also proven 7ffffffb0000 is the start of vDSO in the process VM image.
In LoongArch the -fno-asynchronous-unwind-tables option seems just a MIPS legacy, and MIPS only uses this option to satisfy the MIPS-specific "genvdso" program, per the commit cfd75c2db17e ("MIPS: VDSO: Explicitly use -fno-asynchronous-unwind-tables"). IIRC it indicates some inherent limitation of the MIPS ELF ABI and has nothing to do with LoongArch. So we can simply flip it over to -fasynchronous-unwind-tables and pass --eh-frame-hdr for linking the vDSO, allowing the profilers to unwind the stack for statistics even if the sample point is taken when the PC is in the vDSO.
However simply adjusting the options above would exploit an issue: when the libgcc unwinder saw the invalid GNU_EH_FRAME segment, it silently falled back to a machine-specific routine to match the code pattern of rt_sigreturn() and extract the registers saved in the sigframe if the code pattern is matched. As unwinding from signal handlers is vital for libgcc to support pthread cancellation etc., the fall-back routine had been silently keeping the LoongArch Linux systems functioning since Linux 5.19. But when we start to emit GNU_EH_FRAME with the correct format, fall-back routine will no longer be used and libgcc will fail to unwind the sigframe, and unwinding from signal handlers will no longer work, causing dozens of glibc test failures. To make it possible to unwind from signal handlers again, it's necessary to code the unwind info in __vdso_rt_sigreturn via .cfi_* directives.
The offsets in the .cfi_* directives depend on the layout of struct sigframe, notably the offset of sigcontext in the sigframe. To use the offset in the assembly file, factor out struct sigframe into a header to allow asm-offsets.c to output the offset for assembly.
To work around a long-term issue in the libgcc unwinder (the pc is unconditionally substracted by 1: doing so is technically incorrect for a signal frame), a nop instruction is included with the two real instructions in __vdso_rt_sigreturn in the same FDE PC range. The same hack has been used on x86 for a long time.
--- arch/loongarch/include/asm/linkage.h | 36 ++++++++++++++++++++++++++++++++++ arch/loongarch/include/asm/sigframe.h | 9 ++++++++ arch/loongarch/kernel/asm-offsets.c | 2 + arch/loongarch/kernel/signal.c | 6 ----- arch/loongarch/vdso/Makefile | 4 +-- arch/loongarch/vdso/sigreturn.S | 6 ++--- 6 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 arch/loongarch/include/asm/sigframe.h
arch/loongarch/include/asm/linkage.h +36 -0
@@ -41,4 +41,40 @@ .cfi_endproc; \SYM_END (name,SYM_T_NONE ) +/* +* This is for the signal handler trampoline, which is used as the return +* address of the signal handlers in userspace instead of called normally. +* The long standing libgcc bug https://gcc.gnu.org/PR124050 requires a +* nop between .cfi_startproc and the actual address of the trampoline, so +* we cannot simply use SYM_FUNC_START. +* +* This wrapper also contains all the .cfi_* directives for recovering +* the content of the GPRs and the "return address" (where the rt_sigreturn +* syscall will jump to), assuming there is a struct rt_sigframe (where +* a struct sigcontext containing those information we need to recover) at +* $sp. The "DWARF for the LoongArch(TM) Architecture" manual states +* column 0 is for $zero, but it does not make too much sense to +* save/restore the hardware zero register. Repurpose this column here +* for the return address (here it's not the content of $ra we cannot use +* the default column 3). + */ +#define SYM_SIGFUNC_START (name) \ + .cfi_startproc; \ + .cfi_signal_frame; \ + .cfi_def_cfa 3, RT_SIGFRAME_SC; \ + .cfi_return_column 0; \ + .cfi_offset 0, SC_PC; \ + \ + .irp num, 1, 2, 3, 4, 5, 6, 7, 8, \ + 9, 10, 11, 12, 13, 14, 15, 16, \ + 17, 18, 19, 20, 21, 22, 23, 24, \ + 25, 26, 27, 28, 29, 30, 31; \ + .cfi_offset \num, SC_REGS + \num * SZREG; \ + .endr; \ + \ + nop; \ + SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) + +#define SYM_SIGFUNC_END (name) SYM_FUNC_END(name) +#endif
arch/loongarch/include/asm/sigframe.h new file +9 -0
@@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#include <asm/siginfo.h> +#include <asm/ucontext.h> + +struct rt_sigframe { +struct siginfo rs_info; +struct ucontext rs_uctx; +};
arch/loongarch/kernel/asm-offsets.c +2 -0
@@ -16,6 +16,7 @@#include <asm/ptrace.h> #include <asm/processor.h> #include <asm/ftrace.h> +#include <asm/sigframe.h> static void __used output_ptreg_defines (void ) {
@@ -219,6 +220,7 @@ static void __used output_sc_defines(voiCOMMENT ("Linux sigcontext offsets." );OFFSET (SC_REGS , sigcontext, sc_regs);OFFSET (SC_PC , sigcontext, sc_pc); +OFFSET (RT_SIGFRAME_SC , rt_sigframe, rs_uctx.uc_mcontext);BLANK (); }
arch/loongarch/kernel/signal.c +1 -5
@@ -35,6 +35,7 @@#include <asm/cpu-features.h> #include <asm/fpu.h> #include <asm/lbt.h> +#include <asm/sigframe.h> #include <asm/ucontext.h> #include <asm/vdso.h>
@@ -51,11 +52,6 @@#define lock_lbt_owner () ({ preempt_disable(); pagefault_disable(); })#define unlock_lbt_owner () ({ pagefault_enable(); preempt_enable(); }) -struct rt_sigframe { -struct siginfo rs_info; -struct ucontext rs_uctx; -}; -struct _ctx_layout {struct sctx_info *addr;unsigned int size;
arch/loongarch/vdso/Makefile +2 -2
@@ -21,7 +21,7 @@ cflags-vdso := $(ccflags-vdso) \ $(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \ -std=gnu11 -O2 -g -fno-strict-aliasing -fno-common -fno-builtin \ -fno-stack-protector -fno-jump-tables -DDISABLE_BRANCH_PROFILING \ - $(call cc-option, -fno-asynchronous-unwind-tables) \ + $(call cc-option, -fasynchronous-unwind-tables) \ $(call cc-option, -fno-stack-protector) aflags-vdso := $(ccflags-vdso) \ -D__ASSEMBLY__ -Wa,-gdwarf-2
@@ -36,7 +36,7 @@ endif # VDSO linker flags. ldflags-y := -Bsymbolic --no-undefined -soname=linux-vdso.so.1 \ - $(filter -E%,$(KBUILD_CFLAGS)) -shared --build-id -T + $(filter -E%,$(KBUILD_CFLAGS)) -shared --build-id --eh-frame-hdr -T # # Shared build commands.
arch/loongarch/vdso/sigreturn.S +3 -3
@@ -12,13 +12,13 @@ #include <asm/regdef.h> #include <asm/asm.h> +#include <asm/asm-offsets.h> .section .text - .cfi_sections .debug_frame -SYM_FUNC_START(__vdso_rt_sigreturn) +SYM_SIGFUNC_START(__vdso_rt_sigreturn) li.w a7, __NR_rt_sigreturn syscall 0 -SYM_FUNC_END(__vdso_rt_sigreturn) +SYM_SIGFUNC_END(__vdso_rt_sigreturn)
Greg Kroah-Hartman
maintainerPATCH 205/244 spi: tegra210-quad: Protect curr_xfer check in IRQ handler
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit edf9088b6e1d6d88982db7eb5e736a0e4fbcc09e ]
Now that all other accesses to curr_xfer are done under the lock, protect the curr_xfer NULL check in tegra_qspi_isr_thread() with the spinlock. Without this protection, the following race can occur:
CPU0 (ISR thread) CPU1 (timeout path)
---------------- -------------------
if (!tqspi->curr_xfer)
// sees non-NULL
spin_lock()
tqspi->curr_xfer = NULL
spin_unlock()
handle_*_xfer()
spin_lock()
t = tqspi->curr_xfer // NULL!
... t->len ... // NULL dereference!With this patch, all curr_xfer accesses are now properly synchronized.
Although all accesses to curr_xfer are done under the lock, in tegra_qspi_isr_thread() it checks for NULL, releases the lock and reacquires it later in handle_cpu_based_xfer()/handle_dma_based_xfer(). There is a potential for an update in between, which could cause a NULL pointer dereference.
To handle this, add a NULL check inside the handlers after acquiring the lock. This ensures that if the timeout path has already cleared curr_xfer, the handler will safely return without dereferencing the NULL pointer.
--- drivers/spi/spi-tegra210-quad.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
drivers/spi/spi-tegra210-quad.c +20 -0
@@ -1351,6 +1351,11 @@ static irqreturn_t handle_cpu_based_xferspin_lock_irqsave (&tqspi->lock, flags); t = tqspi->curr_xfer; +if (!t) { +spin_unlock_irqrestore (&tqspi->lock, flags); +return IRQ_HANDLED ; + } +if (tqspi->tx_status || tqspi->rx_status) {tegra_qspi_handle_error (tqspi);complete (&tqspi->xfer_completion);
@@ -1419,6 +1424,11 @@ static irqreturn_t handle_dma_based_xferspin_lock_irqsave (&tqspi->lock, flags); t = tqspi->curr_xfer; +if (!t) { +spin_unlock_irqrestore (&tqspi->lock, flags); +return IRQ_HANDLED ; + } +if (err) {tegra_qspi_dma_unmap_xfer (tqspi, t);tegra_qspi_handle_error (tqspi);
@@ -1457,6 +1467,7 @@ exit:static irqreturn_t tegra_qspi_isr_thread (int irq,void *context_data) {struct tegra_qspi *tqspi = context_data; +unsigned long flags;u32 status;/*
@@ -1474,7 +1485,9 @@ static irqreturn_t tegra_qspi_isr_thread* If no transfer is in progress, check if this was a real interrupt * that the timeout handler already processed, or a spurious one. */ +spin_lock_irqsave (&tqspi->lock, flags);if (!tqspi->curr_xfer) { +spin_unlock_irqrestore (&tqspi->lock, flags);/* Spurious interrupt - transfer not ready */ if (!(status &QSPI_RDY ))return IRQ_NONE ;
@@ -1491,7 +1504,14 @@ static irqreturn_t tegra_qspi_isr_thread tqspi->rx_status = tqspi->status_reg & (QSPI_RX_FIFO_OVF |QSPI_RX_FIFO_UNF );tegra_qspi_mask_clear_irq (tqspi); +spin_unlock_irqrestore (&tqspi->lock, flags); +/* +* Lock is released here but handlers safely re-check curr_xfer under +* lock before dereferencing. +* DMA handler also needs to sleep in wait_for_completion_*(), which +* cannot be done while holding spinlock. +*/ if (!tqspi->is_curr_dma_xfer)return handle_cpu_based_xfer (tqspi);
Greg Kroah-Hartman
maintainerPATCH 206/244 media: nxp: imx8-isi: Fix streaming cleanup on release
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Richard Leitner <[email protected]>
[ Upstream commit 47773031a148ad7973b809cc7723cba77eda2b42 ]
The current implementation unconditionally calls mxc_isi_video_cleanup_streaming() in mxc_isi_video_release(). This can lead to situations where any release call (like from a simple "v4l2-ctl -l") may release a currently streaming queue when called on such a device.
This is reproducible on an i.MX8MP board by streaming from an ISI capture device using gstreamer:
gst-launch-1.0 -v v4l2src device=/dev/videoX ! \ video/x-raw,format=GRAY8,width=1280,height=800,framerate=1/120 ! \ fakesink
While this stream is running, querying the caps of the same device provokes the error state:
v4l2-ctl -l -d /dev/videoX
This results in the following trace:
[ 155.452152] ------------[ cut here ]------------ [ 155.452163] WARNING: CPU: 0 PID: 1708 at drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c:713 mxc_isi_pipe_irq_handler+0x19c/0x1b0 [imx8_isi] [ 157.004248] Modules linked in: cfg80211 rpmsg_ctrl rpmsg_char rpmsg_tty virtio_rpmsg_bus rpmsg_ns rpmsg_core rfkill nft_ct nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 nf_tables mcp251x6 [ 157.053499] CPU: 0 UID: 0 PID: 1708 Comm: python3 Not tainted 6.15.4-00114-g1f61ca5cad76 #1 PREEMPT [ 157.064369] Hardware name: imx8mp_board_01 (DT) [ 157.068205] pstate: 400000c5 (nZcv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 157.075169] pc : mxc_isi_pipe_irq_handler+0x19c/0x1b0 [imx8_isi] [ 157.081195] lr : mxc_isi_pipe_irq_handler+0x38/0x1b0 [imx8_isi] [ 157.087126] sp : ffff800080003ee0 [ 157.090438] x29: ffff800080003ee0 x28: ffff0000c3688000 x27: 0000000000000000 [ 157.097580] x26: 0000000000000000 x25: ffff0000c1e7ac00 x24: ffff800081b5ad50 [ 157.104723] x23: 00000000000000d1 x22: 0000000000000000 x21: ffff0000c25e4000 [ 157.111866] x20: 0000000060000200 x19: ffff80007a0608d0 x18: 0000000000000000 [ 157.119008] x17: ffff80006a4e3000 x16: ffff800080000000 x15: 0000000000000000 [ 157.126146] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ 157.133287] x11: 0000000000000040 x10: ffff0000c01445f0 x9 : ffff80007a053a38 [ 157.140425] x8 : ffff0000c04004b8 x7 : 0000000000000000 x6 : 0000000000000000 [ 157.147567] x5 : ffff0000c0400490 x4 : ffff80006a4e3000 x3 : ffff0000c25e4000 [ 157.154706] x2 : 0000000000000000 x1 : ffff8000825c0014 x0 : 0000000060000200 [ 157.161850] Call trace: [ 157.164296] mxc_isi_pipe_irq_handler+0x19c/0x1b0 [imx8_isi] (P) [ 157.170319] __handle_irq_event_percpu+0x58/0x218 [ 157.175029] handle_irq_event+0x54/0xb8 [ 157.178867] handle_fasteoi_irq+0xac/0x248 [ 157.182968] handle_irq_desc+0x48/0x68 [ 157.186723] generic_handle_domain_irq+0x24/0x38 [ 157.191346] gic_handle_irq+0x54/0x120 [ 157.195098] call_on_irq_stack+0x24/0x30 [ 157.199027] do_interrupt_handler+0x88/0x98 [ 157.203212] el0_interrupt+0x44/0xc0 [ 157.206792] __el0_irq_handler_common+0x18/0x28 [ 157.211328] el0t_64_irq_handler+0x10/0x20 [ 157.215429] el0t_64_irq+0x198/0x1a0 [ 157.219009] ---[ end trace 0000000000000000 ]---
Address this issue by moving the streaming preparation and cleanup to the vb2 .prepare_streaming() and .unprepare_streaming() operations. This also simplifies the driver by allowing direct usage of the vb2_ioctl_streamon() and vb2_ioctl_streamoff() helpers, and removal of the manual cleanup from mxc_isi_video_release().
--- drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c | 156 +++++++------------ 1 file changed, 58 insertions(+), 98 deletions(-)
drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +58 -98
@@ -937,6 +937,49 @@ static void mxc_isi_video_init_channel(smxc_isi_channel_set_output_format (pipe, video->fmtinfo, &video->pix); } +static int mxc_isi_vb2_prepare_streaming (struct vb2_queue *q) +{ +struct mxc_isi_video *video =vb2_get_drv_priv (q); +struct media_device *mdev = &video->pipe->isi->media_dev; +struct media_pipeline *pipe; +int ret; + +/* Get a pipeline for the video node and start it. */ + scoped_guard(mutex, &mdev->graph_mutex) { + ret =mxc_isi_pipe_acquire (video->pipe, + &mxc_isi_video_frame_write_done); +if (ret) +return ret; + + pipe =media_entity_pipeline (&video->vdev.entity) + ? : &video->pipe->pipe; + + ret =__video_device_pipeline_start (&video->vdev, pipe); +if (ret) + goto err_release; + } + +/* Verify that the video format matches the output of the subdev. */ + ret =mxc_isi_video_validate_format (video); +if (ret) + goto err_stop; + +/* Allocate buffers for discard operation. */ + ret =mxc_isi_video_alloc_discard_buffers (video); +if (ret) + goto err_stop; + + video->is_streaming = true; + +return 0 ; + +err_stop: +video_device_pipeline_stop (&video->vdev); +err_release: +mxc_isi_pipe_release (video->pipe); +return ret; +} +static int mxc_isi_vb2_start_streaming (struct vb2_queue *q,unsigned int count) {struct mxc_isi_video *video =vb2_get_drv_priv (q);
@@ -985,6 +1028,17 @@ static void mxc_isi_vb2_stop_streaming(smxc_isi_video_return_buffers (video,VB2_BUF_STATE_ERROR ); } +static void mxc_isi_vb2_unprepare_streaming (struct vb2_queue *q) +{ +struct mxc_isi_video *video =vb2_get_drv_priv (q); + +mxc_isi_video_free_discard_buffers (video); +video_device_pipeline_stop (&video->vdev); +mxc_isi_pipe_release (video->pipe); + + video->is_streaming = false; +} +static const struct vb2_ops mxc_isi_vb2_qops = { .queue_setup = mxc_isi_vb2_queue_setup, .buf_init = mxc_isi_vb2_buffer_init,
@@ -992,8 +1046,10 @@ static const struct vb2_ops mxc_isi_vb2_ .buf_queue = mxc_isi_vb2_buffer_queue, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, + .prepare_streaming = mxc_isi_vb2_prepare_streaming, .start_streaming = mxc_isi_vb2_start_streaming, .stop_streaming = mxc_isi_vb2_stop_streaming, + .unprepare_streaming = mxc_isi_vb2_unprepare_streaming, };/* -----------------------------------------------------------------------------
@@ -1147,97 +1203,6 @@ static int mxc_isi_video_s_fmt(struct fireturn 0 ; } -static int mxc_isi_video_streamon (struct file *file,void *priv, -enum v4l2_buf_type type) -{ -struct mxc_isi_video *video =video_drvdata (file); -struct media_device *mdev = &video->pipe->isi->media_dev; -struct media_pipeline *pipe; -int ret; - -if (vb2_queue_is_busy (&video->vb2_q, file)) -return -EBUSY ; - -/* -* Get a pipeline for the video node and start it. This must be done -* here and not in the queue .start_streaming() handler, so that -* pipeline start errors can be reported from VIDIOC_STREAMON and not -* delayed until subsequent VIDIOC_QBUF calls. -*/ -mutex_lock (&mdev->graph_mutex); - - ret =mxc_isi_pipe_acquire (video->pipe, &mxc_isi_video_frame_write_done); -if (ret) { -mutex_unlock (&mdev->graph_mutex); -return ret; - } - - pipe =media_entity_pipeline (&video->vdev.entity) ? : &video->pipe->pipe; - - ret =__video_device_pipeline_start (&video->vdev, pipe); -if (ret) { -mutex_unlock (&mdev->graph_mutex); - goto err_release; - } - -mutex_unlock (&mdev->graph_mutex); - -/* Verify that the video format matches the output of the subdev. */ - ret =mxc_isi_video_validate_format (video); -if (ret) - goto err_stop; - -/* Allocate buffers for discard operation. */ - ret =mxc_isi_video_alloc_discard_buffers (video); -if (ret) - goto err_stop; - - ret =vb2_streamon (&video->vb2_q, type); -if (ret) - goto err_free; - - video->is_streaming = true; - -return 0 ; - -err_free: -mxc_isi_video_free_discard_buffers (video); -err_stop: -video_device_pipeline_stop (&video->vdev); -err_release: -mxc_isi_pipe_release (video->pipe); -return ret; -} - -static void mxc_isi_video_cleanup_streaming (struct mxc_isi_video *video) -{ -lockdep_assert_held (&video->lock); - -if (!video->is_streaming) -return ; - -mxc_isi_video_free_discard_buffers (video); -video_device_pipeline_stop (&video->vdev); -mxc_isi_pipe_release (video->pipe); - - video->is_streaming = false; -} - -static int mxc_isi_video_streamoff (struct file *file,void *priv, -enum v4l2_buf_type type) -{ -struct mxc_isi_video *video =video_drvdata (file); -int ret; - - ret =vb2_ioctl_streamoff (file, priv, type); -if (ret) -return ret; - -mxc_isi_video_cleanup_streaming (video); - -return 0 ; -} -static int mxc_isi_video_enum_framesizes (struct file *file,void *priv,struct v4l2_frmsizeenum *fsize) {
@@ -1293,9 +1258,8 @@ static const struct v4l2_ioctl_ops mxc_i .vidioc_expbuf = vb2_ioctl_expbuf, .vidioc_prepare_buf = vb2_ioctl_prepare_buf, .vidioc_create_bufs = vb2_ioctl_create_bufs, - - .vidioc_streamon = mxc_isi_video_streamon, - .vidioc_streamoff = mxc_isi_video_streamoff, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_enum_framesizes = mxc_isi_video_enum_framesizes,
@@ -1334,10 +1298,6 @@ static int mxc_isi_video_release(structif (ret)dev_err (video->pipe->isi->dev,"%s fail\n" , __func__); -mutex_lock (&video->lock); -mxc_isi_video_cleanup_streaming (video); -mutex_unlock (&video->lock); -pm_runtime_put (video->pipe->isi->dev);return ret; }
Greg Kroah-Hartman
maintainerPATCH 207/244 mm/damon/sysfs: check contexts->nr before accessing contexts_arr[0]
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Josh Law <[email protected]>
commit 1bfe9fb5ed2667fb075682408b776b5273162615 upstream.
Multiple sysfs command paths dereference contexts_arr[0] without first verifying that kdamond->contexts->nr == 1. A user can set nr_contexts to 0 via sysfs while DAMON is running, causing NULL pointer dereferences.
In more detail, the issue can be triggered by privileged users like below.
First, start DAMON and make contexts directory empty (kdamond->contexts->nr == 0).
# damo start
# cd /sys/kernel/mm/damon/admin/kdamonds/0
# echo 0 > contexts/nr_contextsThen, each of below commands will cause the NULL pointer dereference.
# echo update_schemes_stats > state
# echo update_schemes_tried_regions > state
# echo update_schemes_tried_bytes > state
# echo update_schemes_effective_quotas > state
# echo update_tuned_intervals > stateGuard all commands (except OFF) at the entry point of damon_sysfs_handle_cmd().
--- mm/damon/sysfs.c | 3 +++ 1 file changed, 3 insertions(+)
mm/damon/sysfs.c +3 -0
@@ -1548,6 +1548,9 @@ static int damon_sysfs_handle_cmd(enum d {bool need_wait = true; +if (cmd !=DAMON_SYSFS_CMD_OFF && kdamond->contexts->nr !=1 ) +return -EINVAL ; +/* Handle commands that doesn't access DAMON context-internal data */ switch (cmd) {case DAMON_SYSFS_CMD_ON :
Greg Kroah-Hartman
maintainerPATCH 208/244 rust: pin-init: add references to previously initialized fields
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benno Lossin <[email protected]>
commit 42415d163e5df6db799c7de6262d707e402c2c7e upstream.
After initializing a field in an initializer macro, create a variable holding a reference that points at that field. The type is either Pin<&mut T> or &mut T depending on the field's structural pinning kind.
[ Removed the devres changes, because devres is not present in 6.12.y and
earlier. Also adjusted paths in the macro to account for the fact that pin-init is part of the kernel crate in 6.12.y and earlier. - Benno ]
--- rust/kernel/init/macros.rs | 149 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 115 insertions(+), 34 deletions(-)
rust/kernel/init/macros.rs +115 -34
@@ -985,38 +985,56 @@ macro_rules! __pin_data { @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?), @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?), ) => { - // For every field, we create a projection function according to its projection type. If a - // field is structurally pinned, then it must be initialized via `PinInit`, if it is not - // structurally pinned, then it can be initialized via `Init`. - // - // The functions are `unsafe` to prevent accidentally calling them. - #[allow(dead_code)] - #[expect(clippy::missing_safety_doc)] - impl<$($impl_generics)*> $pin_data<$($ty_generics)*> - where $($whr)* - { - $( - $(#[$($p_attr)*])* - $pvis unsafe fn $p_field<E>( - self, - slot: *mut $p_type, - init: impl $crate::init::PinInit<$p_type, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: TODO. - unsafe { $crate::init::PinInit::__pinned_init(init, slot) } - } - )* - $( - $(#[$($attr)*])* - $fvis unsafe fn $field<E>( - self, - slot: *mut $type, - init: impl $crate::init::Init<$type, E>, - ) -> ::core::result::Result<(), E> { - // SAFETY: TODO. - unsafe { $crate::init::Init::__init(init, slot) } - } - )* + $crate::macros::paste! { +// For every field, we create a projection function according to its projection type. If a +// field is structurally pinned, then it must be initialized via `PinInit`, if it is not +// structurally pinned, then it can be initialized via `Init`. +// +// The functions are `unsafe` to prevent accidentally calling them. +#[allow(dead_code, non_snake_case)] +#[expect(clippy::missing_safety_doc)] +impl <$($impl_generics)*> $pin_data<$($ty_generics)*> +where $($whr)* + { + $( + $(#[$($p_attr)*])* + $pvisunsafe fn $p_field<E >( + self, + slot: *mut $p_type, + init:impl $crate ::init::PinInit <$p_type,E >, + ) -> ::core::result::Result <(),E > { +// SAFETY: TODO. +unsafe { $crate ::init::PinInit ::__pinned_init (init ,slot ) } + } + + $(#[$($p_attr)*])* + $pvisunsafe fn [<__project_ $p_field>]<'__slot>( + self, + slot: &'__slotmut $p_type, + ) -> ::core::pin::Pin <&'__slotmut $p_type> { +unsafe { ::core::pin::Pin ::new_unchecked (slot ) } + } + )* + $( + $(#[$($attr)*])* + $fvisunsafe fn $field<E >( + self, + slot: *mut $type , + init:impl $crate ::init::Init <$type ,E >, + ) -> ::core::result::Result <(),E > { +// SAFETY: TODO. +unsafe { $crate ::init::Init ::__init (init ,slot ) } + } + + $(#[$($attr)*])* + $fvisunsafe fn [<__project_ $field>]<'__slot>( + self, + slot: &'__slotmut $type , + ) -> &'__slotmut $type { +slot + } + )* + } } }; }
@@ -1213,6 +1231,13 @@ macro_rules! __init_internal {// return when an error/panic occurs. // We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`. unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? }; +// SAFETY: +// - the project function does the correct field projection, +// - the field has been initialized, +// - the reference is only valid until the end of the initializer. +#[allow(unused_variables, unused_assignments)] + let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) }); +// Create the drop guard: // // We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1244,6 +1269,14 @@ macro_rules! __init_internal {// SAFETY: `slot` is valid, because we are inside of an initializer closure, we // return when an error/panic occurs. unsafe { $crate::init::Init ::__init (init, ::core::ptr::addr_of_mut!((*$slot).$field))? }; + +// SAFETY: +// - the field is not structurally pinned, since the line above must compile, +// - the field has been initialized, +// - the reference is only valid until the end of the initializer. +#[allow(unused_variables, unused_assignments)] + let $field = unsafe { &mut (*$slot).$field }; +// Create the drop guard: // // We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1262,7 +1295,7 @@ macro_rules! __init_internal { ); } }; - (init_slot($($use_data:ident)?): + (init_slot(): // No `use_data`, so all fields are not structurally pinned @data($data:ident), @slot($slot:ident), @guards($($guards:ident,)*),
@@ -1276,6 +1309,15 @@ macro_rules! __init_internal {// SAFETY: The memory at `slot` is uninitialized. unsafe { ::core::ptr::write (::core::ptr::addr_of_mut!((*$slot).$field), $field) }; } + +#[allow(unused_variables, unused_assignments)] +// SAFETY: +// - the field is not structurally pinned, since no `use_data` was required to create this +// initializer, +// - the field has been initialized, +// - the reference is only valid until the end of the initializer. + let $field = unsafe { &mut (*$slot).$field }; +// Create the drop guard: // // We rely on macro hygiene to make it impossible for users to access this local variable.
@@ -1286,7 +1328,46 @@ macro_rules! __init_internal { $crate::init::__internal::DropGuard ::new (::core::ptr::addr_of_mut!((*$slot).$field)) }; - $crate::__init_internal!(init_slot ($($use_data)?): + $crate::__init_internal!(init_slot (): + @data ($data), + @slot ($slot), + @guards ([< __ $field_guard >], $($guards,)*), + @munch_fields ($($rest)*), + ); + } + }; + (init_slot($use_data:ident): + @data($data:ident), + @slot($slot:ident), + @guards($($guards:ident,)*), +// Init by-value. + @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), + ) => { + { + $(let $field = $val;)? +// Initialize the field. +// +// SAFETY: The memory at `slot` is uninitialized. +unsafe { ::core::ptr::write (::core::ptr::addr_of_mut!((*$slot).$field), $field) }; + } +// SAFETY: +// - the project function does the correct field projection, +// - the field has been initialized, +// - the reference is only valid until the end of the initializer. +#[allow(unused_variables, unused_assignments)] + let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) }); + +// Create the drop guard: +// +// We rely on macro hygiene to make it impossible for users to access this local variable. +// We use `paste!` to create new hygiene for `$field`. + $crate::macros::paste! { +// SAFETY: We forget the guard later when initialization has succeeded. +let [< __ $field_guard >] =unsafe { + $crate ::init::__internal::DropGuard ::new (::core::ptr::addr_of_mut!((*$slot).$field)) + }; + + $crate ::__init_internal!(init_slot ($use_data): @data($data), @slot ($slot), @guards ([< __ $field_guard >], $($guards,)*),
Greg Kroah-Hartman
maintainerPATCH 209/244 rust: pin-init: internal: init: document load-bearing fact of field accessors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benno Lossin <[email protected]>
commit 580cc37b1de4fcd9997c48d7080e744533f09f36 upstream.
The functions [Pin]Init::__[pinned_]init and ptr::write called from the init! macro require the passed pointer to be aligned. This fact is ensured by the creation of field accessors to previously initialized fields.
Since we missed this very important fact from the beginning [1], document it in the code.
[ Moved changes to the declarative macro, because 6.19.y and earlier do not
have syn. Also duplicated the comment for all field accessor creations.
- Benno ]
--- rust/kernel/init/macros.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
rust/kernel/init/macros.rs +16 -0
@@ -1231,6 +1231,10 @@ macro_rules! __init_internal {// return when an error/panic occurs. // We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`. unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? }; +// NOTE: the field accessor ensures that the initialized field is properly aligned. +// Unaligned fields will cause the compiler to emit E0793. We do not support +// unaligned fields since `Init::__init` requires an aligned pointer; the call to +// `ptr::write` below has the same requirement. // SAFETY: // - the project function does the correct field projection, // - the field has been initialized,
@@ -1270,6 +1274,10 @@ macro_rules! __init_internal {// return when an error/panic occurs. unsafe { $crate::init::Init ::__init (init, ::core::ptr::addr_of_mut!((*$slot).$field))? }; +// NOTE: the field accessor ensures that the initialized field is properly aligned. +// Unaligned fields will cause the compiler to emit E0793. We do not support +// unaligned fields since `Init::__init` requires an aligned pointer; the call to +// `ptr::write` below has the same requirement. // SAFETY: // - the field is not structurally pinned, since the line above must compile, // - the field has been initialized,
@@ -1310,6 +1318,10 @@ macro_rules! __init_internal {unsafe { ::core::ptr::write (::core::ptr::addr_of_mut!((*$slot).$field), $field) }; } +// NOTE: the field accessor ensures that the initialized field is properly aligned. +// Unaligned fields will cause the compiler to emit E0793. We do not support +// unaligned fields since `Init::__init` requires an aligned pointer; the call to +// `ptr::write` below has the same requirement. #[allow(unused_variables, unused_assignments)] // SAFETY: // - the field is not structurally pinned, since no `use_data` was required to create this
@@ -1350,6 +1362,10 @@ macro_rules! __init_internal {// SAFETY: The memory at `slot` is uninitialized. unsafe { ::core::ptr::write (::core::ptr::addr_of_mut!((*$slot).$field), $field) }; } +// NOTE: the field accessor ensures that the initialized field is properly aligned. +// Unaligned fields will cause the compiler to emit E0793. We do not support +// unaligned fields since `Init::__init` requires an aligned pointer; the call to +// `ptr::write` below has the same requirement. // SAFETY: // - the project function does the correct field projection, // - the field has been initialized,
Greg Kroah-Hartman
maintainerPATCH 210/244 ovl: Use str_on_off() helper in ovl_show_options()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thorsten Blum <[email protected]>
commit 50e638beb67e020a9124d77bd8a88bde3cd380e3 upstream.
Remove hard-coded strings by using the str_on_off() helper function.
--- fs/overlayfs/params.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
fs/overlayfs/params.c +4 -5
@@ -987,17 +987,16 @@ int ovl_show_options(struct seq_file *m,seq_printf (m,",redirect_dir=%s" ,ovl_redirect_mode (&ofs->config));if (ofs->config.index != ovl_index_def) -seq_printf (m,",index=%s" , ofs->config.index ?"on" :"off" ); +seq_printf (m,",index=%s" ,str_on_off (ofs->config.index));if (ofs->config.uuid !=ovl_uuid_def ())seq_printf (m,",uuid=%s" ,ovl_uuid_mode (&ofs->config));if (ofs->config.nfs_export != ovl_nfs_export_def) -seq_printf (m,",nfs_export=%s" , ofs->config.nfs_export ? -"on" :"off" ); +seq_printf (m,",nfs_export=%s" , +str_on_off (ofs->config.nfs_export));if (ofs->config.xino !=ovl_xino_def () && !ovl_same_fs (ofs))seq_printf (m,",xino=%s" ,ovl_xino_mode (&ofs->config));if (ofs->config.metacopy != ovl_metacopy_def) -seq_printf (m,",metacopy=%s" , - ofs->config.metacopy ?"on" :"off" ); +seq_printf (m,",metacopy=%s" ,str_on_off (ofs->config.metacopy));if (ofs->config.ovl_volatile)seq_puts (m,",volatile" );if (ofs->config.userxattr)
Greg Kroah-Hartman
maintainerPATCH 211/244 ovl: make fsync after metadata copy-up opt-in mount option
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fei Lv <[email protected]>
commit 1f6ee9be92f8df85a8c9a5a78c20fd39c0c21a95 upstream.
Commit 7d6899fb69d25 ("ovl: fsync after metadata copy-up") was done to fix durability of overlayfs copy up on an upper filesystem which does not enforce ordering on storing of metadata changes (e.g. ubifs).
In an earlier revision of the regressing commit by Lei Lv, the metadata fsync behavior was opt-in via a new "fsync=strict" mount option. We were hoping that the opt-in mount option could be avoided, so the change was only made to depend on metacopy=off, in the hope of not hurting performance of metadata heavy workloads, which are more likely to be using metacopy=on.
This hope was proven wrong by a performance regression report from Google COS workload after upgrade to kernel 6.12.
This is an adaptation of Lei's original "fsync=strict" mount option to the existing upstream code.
The new mount option is mutually exclusive with the "volatile" mount option, so the latter is now an alias to the "fsync=volatile" mount option.
Depends: 50e638beb67e0 ("ovl: Use str_on_off() helper in ovl_show_options()")
--- Documentation/filesystems/overlayfs.rst | 50 ++++++++++++++++++++++++++++++++ fs/overlayfs/copy_up.c | 6 +-- fs/overlayfs/overlayfs.h | 21 +++++++++++++ fs/overlayfs/ovl_entry.h | 7 ---- fs/overlayfs/params.c | 33 +++++++++++++++++---- fs/overlayfs/super.c | 2 - 6 files changed, 104 insertions(+), 15 deletions(-)
Documentation/filesystems/overlayfs.rst +50 -0
@@ -745,6 +745,56 @@ controlled by the "uuid" mount option, w mounted with "uuid=on". +Durability and copy up +---------------------- + +The fsync(2) system call ensures that the data and metadata of a file +are safely written to the backing storage, which is expected to +guarantee the existence of the information post system crash. + +Without an fsync(2) call, there is no guarantee that the observed +data after a system crash will be either the old or the new data, but +in practice, the observed data after crash is often the old or new data +or a mix of both. + +When an overlayfs file is modified for the first time, copy up will +create a copy of the lower file and its parent directories in the upper +layer. Since the Linux filesystem API does not enforce any particular +ordering on storing changes without explicit fsync(2) calls, in case +of a system crash, the upper file could end up with no data at all +(i.e. zeros), which would be an unusual outcome. To avoid this +experience, overlayfs calls fsync(2) on the upper file before completing +data copy up with rename(2) or link(2) to make the copy up "atomic". + +By default, overlayfs does not explicitly call fsync(2) on copied up +directories or on metadata-only copy up, so it provides no guarantee to +persist the user's modification unless the user calls fsync(2). +The fsync during copy up only guarantees that if a copy up is observed +after a crash, the observed data is not zeroes or intermediate values +from the copy up staging area. + +On traditional local filesystems with a single journal (e.g. ext4, xfs), +fsync on a file also persists the parent directory changes, because they +are usually modified in the same transaction, so metadata durability during +data copy up effectively comes for free. Overlayfs further limits risk by +disallowing network filesystems as upper layer. + +Overlayfs can be tuned to prefer performance or durability when storing +to the underlying upper layer. This is controlled by the "fsync" mount +option, which supports these values: + +- "auto": (default) + Call fsync(2) on upper file before completion of data copy up. + No explicit fsync(2) on directory or metadata-only copy up. +- "strict": + Call fsync(2) on upper file and directories before completion of any + copy up. +- "volatile": [*] + Prefer performance over durability (see `Volatile mount`_) + +[*] The mount option "volatile" is an alias to "fsync=volatile". + + Volatile mount --------------
fs/overlayfs/copy_up.c +3 -3
@@ -1160,15 +1160,15 @@ static int ovl_copy_up_one(struct dentryreturn -EOVERFLOW ;/* -* With metacopy disabled, we fsync after final metadata copyup, for +* With "fsync=strict", we fsync after final metadata copyup, for * both regular files and directories to get atomic copyup semantics * on filesystems that do not use strict metadata ordering (e.g. ubifs). * -* With metacopy enabled we want to avoid fsync on all meta copyup +* By default, we want to avoid fsync on all meta copyup, because * that will hurt performance of workloads such as chown -R, so we * only fsync on data copyup as legacy behavior. */ - ctx.metadata_fsync = !OVL_FS (dentry->d_sb)->config.metacopy && + ctx.metadata_fsync =ovl_should_sync_metadata (OVL_FS (dentry->d_sb)) && (S_ISREG (ctx.stat.mode) ||S_ISDIR (ctx.stat.mode)); ctx.metacopy =ovl_need_meta_copy_up (dentry, ctx.stat.mode, flags);
fs/overlayfs/overlayfs.h +21 -0
@@ -99,6 +99,12 @@ enum { OVL_VERITY_REQUIRE, }; +enum { +OVL_FSYNC_VOLATILE , +OVL_FSYNC_AUTO , +OVL_FSYNC_STRICT , +}; +/* * The tuple (fh,uuid) is a universal unique identifier for a copy up origin, * where:
@@ -618,6 +624,21 @@ static inline bool ovl_xino_warn(structreturn ofs->config.xino ==OVL_XINO_ON ; } +static inline bool ovl_should_sync (struct ovl_fs *ofs) +{ +return ofs->config.fsync_mode !=OVL_FSYNC_VOLATILE ; +} + +static inline bool ovl_should_sync_metadata (struct ovl_fs *ofs) +{ +return ofs->config.fsync_mode ==OVL_FSYNC_STRICT ; +} + +static inline bool ovl_is_volatile (struct ovl_config *config) +{ +return config->fsync_mode ==OVL_FSYNC_VOLATILE ; +} +/* * To avoid regressions in existing setups with overlay lower offline changes, * we allow lower changes only if none of the new features are used.
fs/overlayfs/ovl_entry.h +1 -6
@@ -18,7 +18,7 @@ struct ovl_config {int xino;bool metacopy;bool userxattr; -bool ovl_volatile; +int fsync_mode; };struct ovl_sb {
@@ -118,11 +118,6 @@ static inline struct ovl_fs *OVL_FS(strureturn (struct ovl_fs *)sb->s_fs_info; } -static inline bool ovl_should_sync (struct ovl_fs *ofs) -{ -return !ofs->config.ovl_volatile; -} -static inline unsigned int ovl_numlower (struct ovl_entry *oe) {return oe ? oe->__numlower :0 ;
fs/overlayfs/params.c +28 -5
@@ -58,6 +58,7 @@ enum ovl_opt { Opt_xino, Opt_metacopy, Opt_verity, + Opt_fsync, Opt_volatile, };
@@ -139,6 +140,23 @@ static int ovl_verity_mode_def(void)return OVL_VERITY_OFF ; } +static const struct constant_table ovl_parameter_fsync[] = { + {"volatile" ,OVL_FSYNC_VOLATILE }, + {"auto" ,OVL_FSYNC_AUTO }, + {"strict" ,OVL_FSYNC_STRICT }, + {} +}; + +static const char *ovl_fsync_mode (struct ovl_config *config) +{ +return ovl_parameter_fsync[config->fsync_mode].name; +} + +static int ovl_fsync_mode_def (void ) +{ +return OVL_FSYNC_AUTO ; +} +const struct fs_parameter_spec ovl_parameter_spec[] = {fsparam_string_empty ("lowerdir" , Opt_lowerdir),fsparam_string ("lowerdir+" , Opt_lowerdir_add),
@@ -154,6 +172,7 @@ const struct fs_parameter_spec ovl_paramfsparam_enum ("xino" , Opt_xino, ovl_parameter_xino),fsparam_enum ("metacopy" , Opt_metacopy, ovl_parameter_bool),fsparam_enum ("verity" , Opt_verity, ovl_parameter_verity), +fsparam_enum ("fsync" , Opt_fsync, ovl_parameter_fsync), fsparam_flag("volatile", Opt_volatile), {} };
@@ -590,8 +609,11 @@ static int ovl_parse_param(struct fs_concase Opt_verity: config->verity_mode = result.uint_32;break ; +case Opt_fsync: + config->fsync_mode = result.uint_32; +break ;case Opt_volatile: - config->ovl_volatile = true; + config->fsync_mode =OVL_FSYNC_VOLATILE ;break ;case Opt_userxattr: config->userxattr = true;
@@ -702,6 +724,7 @@ int ovl_init_fs_context(struct fs_contex ofs->config.nfs_export = ovl_nfs_export_def; ofs->config.xino =ovl_xino_def (); ofs->config.metacopy = ovl_metacopy_def; + ofs->config.fsync_mode =ovl_fsync_mode_def (); fc->s_fs_info = ofs; fc->fs_private = ctx;
@@ -770,9 +793,9 @@ int ovl_fs_params_verify(const struct ov config->index = false; } -if (!config->upperdir && config->ovl_volatile) { +if (!config->upperdir &&ovl_is_volatile (config)) {pr_info ("option \"volatile\" is meaningless in a non-upper mount, ignoring it.\n" ); - config->ovl_volatile = false; + config->fsync_mode =ovl_fsync_mode_def (); }if (!config->upperdir && config->uuid ==OVL_UUID_ON ) {
@@ -997,8 +1020,8 @@ int ovl_show_options(struct seq_file *m,seq_printf (m,",xino=%s" ,ovl_xino_mode (&ofs->config));if (ofs->config.metacopy != ovl_metacopy_def)seq_printf (m,",metacopy=%s" ,str_on_off (ofs->config.metacopy)); -if (ofs->config.ovl_volatile) -seq_puts (m,",volatile" ); +if (ofs->config.fsync_mode !=ovl_fsync_mode_def ()) +seq_printf (m,",fsync=%s" ,ovl_fsync_mode (&ofs->config));if (ofs->config.userxattr)seq_puts (m,",userxattr" );if (ofs->config.verity_mode !=ovl_verity_mode_def ())
fs/overlayfs/super.c +1 -1
@@ -744,7 +744,7 @@ static int ovl_make_workdir(struct super* For volatile mount, create a incompat/volatile/dirty file to keep * track of it. */ -if (ofs->config.ovl_volatile) { +if (ovl_is_volatile (&ofs->config)) { err =ovl_create_volatile_dirty (ofs);if (err <0 ) {pr_err ("Failed to create volatile/dirty file.\n" );
Greg Kroah-Hartman
maintainerPATCH 212/244 xfs: avoid dereferencing log items after push callbacks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuto Ohnuki <[email protected]>
commit 79ef34ec0554ec04bdbafafbc9836423734e1bd6 upstream.
After xfsaild_push_item() calls iop_push(), the log item may have been freed if the AIL lock was dropped during the push. Background inode reclaim or the dquot shrinker can free the log item while the AIL lock is not held, and the tracepoints in the switch statement dereference the log item after iop_push() returns.
Fix this by capturing the log item type, flags, and LSN before calling xfsaild_push_item(), and introducing a new xfs_ail_push_class trace event class that takes these pre-captured values and the ailp pointer instead of the log item pointer.
--- fs/xfs/xfs_trace.h | 36 ++++++++++++++++++++++++++++++++---- fs/xfs/xfs_trans_ail.c | 26 +++++++++++++++++++------- 2 files changed, 51 insertions(+), 11 deletions(-)
fs/xfs/xfs_trace.h +32 -4
@@ -52,6 +52,7 @@#include <linux/tracepoint.h> struct xfs_agf ; +struct xfs_ail ;struct xfs_alloc_arg ;struct xfs_attr_list_context ;struct xfs_buf_log_item ;
@@ -1351,14 +1352,41 @@ TRACE_EVENT(xfs_log_force,DEFINE_EVENT (xfs_log_item_class, name, \ TP_PROTO(struct xfs_log_item *lip), \ TP_ARGS(lip)) -DEFINE_LOG_ITEM_EVENT (xfs_ail_push); -DEFINE_LOG_ITEM_EVENT (xfs_ail_pinned); -DEFINE_LOG_ITEM_EVENT (xfs_ail_locked); -DEFINE_LOG_ITEM_EVENT (xfs_ail_flushing);DEFINE_LOG_ITEM_EVENT (xfs_cil_whiteout_mark);DEFINE_LOG_ITEM_EVENT (xfs_cil_whiteout_skip);DEFINE_LOG_ITEM_EVENT (xfs_cil_whiteout_unpin); +DECLARE_EVENT_CLASS (xfs_ail_push_class, + TP_PROTO(struct xfs_ail *ailp, uint type, unsigned long flags, xfs_lsn_t lsn), +TP_ARGS (ailp, type, flags, lsn), +TP_STRUCT__entry ( + __field(dev_t, dev) + __field(uint, type) + __field(unsigned long, flags) +__field (xfs_lsn_t, lsn) + ), +TP_fast_assign ( + __entry->dev = ailp->ail_log->l_mp->m_super->s_dev; + __entry->type = type; + __entry->flags = flags; + __entry->lsn = lsn; + ), +TP_printk ("dev %d:%d lsn %d/%d type %s flags %s" , +MAJOR (__entry->dev),MINOR (__entry->dev), +CYCLE_LSN (__entry->lsn),BLOCK_LSN (__entry->lsn), +__print_symbolic (__entry->type,XFS_LI_TYPE_DESC ), +__print_flags (__entry->flags,"|" ,XFS_LI_FLAGS )) +) + +#define DEFINE_AIL_PUSH_EVENT (name) \ +DEFINE_EVENT(xfs_ail_push_class, name, \ + TP_PROTO(struct xfs_ail *ailp, uint type, unsigned long flags, xfs_lsn_t lsn), \ + TP_ARGS(ailp, type, flags, lsn)) +DEFINE_AIL_PUSH_EVENT (xfs_ail_push); +DEFINE_AIL_PUSH_EVENT (xfs_ail_pinned); +DEFINE_AIL_PUSH_EVENT (xfs_ail_locked); +DEFINE_AIL_PUSH_EVENT (xfs_ail_flushing); + DECLARE_EVENT_CLASS(xfs_ail_class, TP_PROTO(struct xfs_log_item *lip, xfs_lsn_t old_lsn, xfs_lsn_t new_lsn), TP_ARGS(lip, old_lsn, new_lsn),
fs/xfs/xfs_trans_ail.c +19 -7
@@ -370,6 +370,12 @@ xfsaild_resubmit_item(return XFS_ITEM_SUCCESS ; } +/* +* Push a single log item from the AIL. +* +* @lip may have been released and freed by the time this function returns, +* so callers must not dereference the log item afterwards. +*/ static inline uint xfsaild_push_item( struct xfs_ail *ailp,
@@ -510,7 +516,10 @@ xfsaild_push( lsn = lip->li_lsn;while ((XFS_LSN_CMP (lip->li_lsn, ailp->ail_target) <=0 )) { -int lock_result; +int lock_result; +uint type = lip->li_type; +unsigned long flags = lip->li_flags; +xfs_lsn_t item_lsn = lip->li_lsn;if (test_bit (XFS_LI_FLUSHING , &lip->li_flags)) goto next_item;
@@ -519,14 +528,17 @@ xfsaild_push(* Note that iop_push may unlock and reacquire the AIL lock. We * rely on the AIL cursor implementation to be able to deal with * the dropped lock. +* +* The log item may have been freed by the push, so it must not +* be accessed or dereferenced below this line. */ lock_result =xfsaild_push_item (ailp, lip);switch (lock_result) {case XFS_ITEM_SUCCESS :XFS_STATS_INC (mp, xs_push_ail_success); -trace_xfs_ail_push (lip); +trace_xfs_ail_push (ailp, type, flags, item_lsn); - ailp->ail_last_pushed_lsn = lsn; + ailp->ail_last_pushed_lsn = item_lsn;break ;case XFS_ITEM_FLUSHING :
@@ -542,22 +554,22 @@ xfsaild_push(* AIL is being flushed. */ XFS_STATS_INC (mp, xs_push_ail_flushing); -trace_xfs_ail_flushing (lip); +trace_xfs_ail_flushing (ailp, type, flags, item_lsn); flushing++; - ailp->ail_last_pushed_lsn = lsn; + ailp->ail_last_pushed_lsn = item_lsn;break ;case XFS_ITEM_PINNED :XFS_STATS_INC (mp, xs_push_ail_pinned); -trace_xfs_ail_pinned (lip); +trace_xfs_ail_pinned (ailp, type, flags, item_lsn); stuck++; ailp->ail_log_flush++;break ;case XFS_ITEM_LOCKED :XFS_STATS_INC (mp, xs_push_ail_locked); -trace_xfs_ail_locked (lip); +trace_xfs_ail_locked (ailp, type, flags, item_lsn); stuck++;break ;
Greg Kroah-Hartman
maintainerPATCH 213/244 virt: tdx-guest: Fix handling of host controlled quote buffer length
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zubin Mithra <[email protected]>
commit c3fd16c3b98ed726294feab2f94f876290bf7b61 upstream.
Validate host controlled value quote_buf->out_len that determines how many bytes of the quote are copied out to guest userspace. In TDX environments with remote attestation, quotes are not considered private, and can be forwarded to an attestation server.
Catch scenarios where the host specifies a response length larger than the guest's allocation, or otherwise races modifying the response while the guest consumes it.
This prevents contents beyond the pages allocated for quote_buf (up to TSM_REPORT_OUTBLOB_MAX) from being read out to guest userspace, and possibly forwarded in attestation requests.
Recall that some deployments want per-container configs-tsm-report interfaces, so the leak may cross container protection boundaries, not just local root.
--- drivers/virt/coco/tdx-guest/tdx-guest.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
drivers/virt/coco/tdx-guest/tdx-guest.c +12 -2
@@ -35,6 +35,8 @@#define GET_QUOTE_SUCCESS 0#define GET_QUOTE_IN_FLIGHT 0xffffffffffffffff +#define TDX_QUOTE_MAX_LEN (GET_QUOTE_BUF_SIZE - sizeof(struct tdx_quote_buf)) +/* struct tdx_quote_buf: Format of Quote request buffer. * @version: Quote format version, filled by TD. * @status: Status code of Quote request, filled by VMM.
@@ -162,6 +164,7 @@ static int tdx_report_new(struct tsm_repu8 *buf, *reportdata =NULL , *tdreport =NULL ;struct tdx_quote_buf *quote_buf = quote_data;struct tsm_desc *desc = &report->desc; +u32 out_len;int ret;u64 err;
@@ -226,14 +229,21 @@ static int tdx_report_new(struct tsm_rep goto done; } - buf =kvmemdup (quote_buf->data, quote_buf->out_len,GFP_KERNEL ); + out_len =READ_ONCE (quote_buf->out_len); + +if (out_len >TDX_QUOTE_MAX_LEN ) { + ret = -EFBIG ; + goto done; + } + + buf =kvmemdup (quote_buf->data, out_len,GFP_KERNEL );if (!buf) { ret = -ENOMEM ; goto done; } report->outblob = buf; - report->outblob_len = quote_buf->out_len; + report->outblob_len = out_len;/* * TODO: parse the PEM-formatted cert chain out of the quote buffer when
Greg Kroah-Hartman
maintainerPATCH 214/244 net: add proper RCU protection to /proc/net/ptype
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eric Dumazet <[email protected]>
[ Upstream commit f613e8b4afea0cd17c7168e8b00e25bc8d33175d ]
Yin Fengwei reported an RCU stall in ptype_seq_show() and provided a patch.
Real issue is that ptype_seq_next() and ptype_seq_show() violate RCU rules.
ptype_seq_show() runs under rcu_read_lock(), and reads pt->dev to get device name without any barrier.
At the same time, concurrent writers can remove a packet_type structure (which is correctly freed after an RCU grace period) and clear pt->dev without an RCU grace period.
Define ptype_iter_state to carry a dev pointer along seq_net_private:
struct ptype_iter_state {
struct seq_net_private p;
struct net_device *dev; // added in this patch
};We need to record the device pointer in ptype_get_idx() and ptype_seq_next() so that ptype_seq_show() is safe against concurrent pt->dev changes.
We also need to add full RCU protection in ptype_seq_next(). (Missing READ_ONCE() when reading list.next values)
Many thanks to Dong Chenchen for providing a repro.
--- net/core/net-procfs.c | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-)
net/core/net-procfs.c +33 -16
@@ -168,8 +168,14 @@ static const struct seq_operations softn .show = softnet_seq_show, }; +struct ptype_iter_state { +struct seq_net_private p; +struct net_device *dev; +}; +static void *ptype_get_idx (struct seq_file *seq,loff_t pos) { +struct ptype_iter_state *iter = seq->private;struct list_head *ptype_list =NULL ;struct packet_type *pt =NULL ;struct net_device *dev;
@@ -179,12 +185,16 @@ static void *ptype_get_idx(struct seq_fifor_each_netdev_rcu (seq_file_net (seq), dev) { ptype_list = &dev->ptype_all;list_for_each_entry_rcu (pt, ptype_list, list) { -if (i == pos) +if (i == pos) { + iter->dev = dev;return pt; + } ++i; } } + iter->dev =NULL ; +list_for_each_entry_rcu (pt, &net_hotdata.ptype_all, list) {if (i == pos)return pt;
@@ -210,6 +220,7 @@ static void *ptype_seq_start(struct seq_static void *ptype_seq_next (struct seq_file *seq,void *v,loff_t *pos) { +struct ptype_iter_state *iter = seq->private;struct net_device *dev;struct packet_type *pt;struct list_head *nxt;
@@ -220,20 +231,21 @@ static void *ptype_seq_next(struct seq_freturn ptype_get_idx (seq,0 ); pt = v; - nxt = pt->list.next; -if (pt->dev) { -if (nxt != &pt->dev->ptype_all) + nxt =READ_ONCE (pt->list.next); + dev = iter->dev; +if (dev) { +if (nxt != &dev->ptype_all) goto found; - dev = pt->dev;for_each_netdev_continue_rcu (seq_file_net (seq), dev) { -if (!list_empty (&dev->ptype_all)) { - nxt = dev->ptype_all.next; + nxt =READ_ONCE (dev->ptype_all.next); +if (nxt != &dev->ptype_all) { + iter->dev = dev; goto found; } } - - nxt = net_hotdata.ptype_all.next; + iter->dev =NULL ; + nxt =READ_ONCE (net_hotdata.ptype_all.next); goto ptype_all; }
@@ -242,14 +254,14 @@ ptype_all:if (nxt != &net_hotdata.ptype_all) goto found; hash =0 ; - nxt = ptype_base[0 ].next; + nxt =READ_ONCE (ptype_base[0 ].next); }else hash =ntohs (pt->type) &PTYPE_HASH_MASK ;while (nxt == &ptype_base[hash]) {if (++hash >=PTYPE_HASH_SIZE )return NULL ; - nxt = ptype_base[hash].next; + nxt =READ_ONCE (ptype_base[hash].next); } found: return list_entry(nxt, struct packet_type, list);
@@ -263,19 +275,24 @@ static void ptype_seq_stop(struct seq_fistatic int ptype_seq_show (struct seq_file *seq,void *v) { +struct ptype_iter_state *iter = seq->private;struct packet_type *pt = v; +struct net_device *dev; -if (v ==SEQ_START_TOKEN ) +if (v ==SEQ_START_TOKEN ) {seq_puts (seq,"Type Device Function\n" ); -else if ((!pt->af_packet_net ||net_eq (pt->af_packet_net,seq_file_net (seq))) && - (!pt->dev ||net_eq (dev_net (pt->dev),seq_file_net (seq)))) { +return 0 ; + } + dev = iter->dev; +if ((!pt->af_packet_net ||net_eq (pt->af_packet_net,seq_file_net (seq))) && + (!dev ||net_eq (dev_net (dev),seq_file_net (seq)))) {if (pt->type ==htons (ETH_P_ALL ))seq_puts (seq,"ALL " );else seq_printf (seq,"%04x" ,ntohs (pt->type));seq_printf (seq," %-8s %ps\n" , - pt->dev ? pt->dev->name :"" , pt->func); + dev ? dev->name :"" , pt->func); }return 0 ;
@@ -299,7 +316,7 @@ static int __net_init dev_proc_net_init( &softnet_seq_ops)) goto out_dev;if (!proc_create_net ("ptype" ,0444 , net->proc_net, &ptype_seq_ops, -sizeof (struct seq_net_private ))) +sizeof (struct ptype_iter_state ))) goto out_softnet;if (wext_proc_init (net))
Greg Kroah-Hartman
maintainerPATCH 215/244 landlock: Optimize file path walks and prepare for audit support
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mickaël Salaün <[email protected]>
[ Upstream commit d617f0d72d8041c7099fd04a62db0f0fa5331c1a ]
Always synchronize access_masked_parent* with access_request_parent* according to allowed_parent*. This is required for audit support to be able to get back to the reason of denial.
In a rename/link action, instead of always checking a rule two times for the same parent directory of the source and the destination files, only check it when an action on a child was not already allowed. This also enables us to keep consistent allowed_parent* status, which is required to get back to the reason of denial.
For internal mount points, only upgrade allowed_parent* to true but do not wrongfully set both of them to false otherwise. This is also required to get back to the reason of denial.
This does not impact the current behavior but slightly optimize code and prepare for audit support that needs to know the exact reason why an access was denied.
Stable-dep-of: 49c9e09d9610 ("landlock: Fix handling of disconnected directories")
--- security/landlock/fs.c | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-)
security/landlock/fs.c +27 -17
@@ -849,15 +849,6 @@ static bool is_access_to_paths_allowed( child1_is_directory, layer_masks_parent2, layer_masks_child2, child2_is_directory))) { - allowed_parent1 =scope_to_request ( - access_request_parent1, layer_masks_parent1); - allowed_parent2 =scope_to_request ( - access_request_parent2, layer_masks_parent2); - -/* Stops when all accesses are granted. */ -if (allowed_parent1 && allowed_parent2) -break ; -/* * Now, downgrades the remaining checks from domain * handled accesses to requested accesses.
@@ -865,15 +856,32 @@ static bool is_access_to_paths_allowed( is_dom_check = false; access_masked_parent1 = access_request_parent1; access_masked_parent2 = access_request_parent2; + + allowed_parent1 = + allowed_parent1 || +scope_to_request (access_masked_parent1, + layer_masks_parent1); + allowed_parent2 = + allowed_parent2 || +scope_to_request (access_masked_parent2, + layer_masks_parent2); + +/* Stops when all accesses are granted. */ +if (allowed_parent1 && allowed_parent2) +break ; } rule =find_rule (domain, walker_path.dentry); - allowed_parent1 =landlock_unmask_layers ( - rule, access_masked_parent1, layer_masks_parent1, -ARRAY_SIZE (*layer_masks_parent1)); - allowed_parent2 =landlock_unmask_layers ( - rule, access_masked_parent2, layer_masks_parent2, -ARRAY_SIZE (*layer_masks_parent2)); + allowed_parent1 = allowed_parent1 || +landlock_unmask_layers ( + rule, access_masked_parent1, + layer_masks_parent1, +ARRAY_SIZE (*layer_masks_parent1)); + allowed_parent2 = allowed_parent2 || +landlock_unmask_layers ( + rule, access_masked_parent2, + layer_masks_parent2, +ARRAY_SIZE (*layer_masks_parent2));/* Stops when a rule from each layer grants access. */ if (allowed_parent1 && allowed_parent2)
@@ -897,8 +905,10 @@ jump_up:* access to internal filesystems (e.g. nsfs, which is * reachable through /proc/<pid>/ns/<namespace>). */ - allowed_parent1 = allowed_parent2 = - !!(walker_path.mnt->mnt_flags &MNT_INTERNAL ); +if (walker_path.mnt->mnt_flags &MNT_INTERNAL ) { + allowed_parent1 = true; + allowed_parent2 = true; + }break ; } parent_dentry =dget_parent (walker_path.dentry);
Greg Kroah-Hartman
maintainerPATCH 216/244 landlock: Fix handling of disconnected directories
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mickaël Salaün <[email protected]>
[ Upstream commit 49c9e09d961025b22e61ef9ad56aa1c21b6ce2f1 ]
Disconnected files or directories can appear when they are visible and opened from a bind mount, but have been renamed or moved from the source of the bind mount in a way that makes them inaccessible from the mount point (i.e. out of scope).
Previously, access rights tied to files or directories opened through a disconnected directory were collected by walking the related hierarchy down to the root of the filesystem, without taking into account the mount point because it couldn't be found. This could lead to inconsistent access results, potential access right widening, and hard-to-debug renames, especially since such paths cannot be printed.
For a sandboxed task to create a disconnected directory, it needs to have write access (i.e. FS_MAKE_REG, FS_REMOVE_FILE, and FS_REFER) to the underlying source of the bind mount, and read access to the related
mount point. Because a sandboxed task cannot acquire more access
rights than those defined by its Landlock domain, this could lead to inconsistent access rights due to missing permissions that should be inherited from the mount point hierarchy, while inheriting permissions from the filesystem hierarchy hidden by this mount point instead.
Landlock now handles files and directories opened from disconnected directories by taking into account the filesystem hierarchy when the mount point is not found in the hierarchy walk, and also always taking into account the mount point from which these disconnected directories were opened. This ensures that a rename is not allowed if it would widen access rights [1].
The rationale is that, even if disconnected hierarchies might not be visible or accessible to a sandboxed task, relying on the collected access rights from them improves the guarantee that access rights will not be widened during a rename because of the access right comparison between the source and the destination (see LANDLOCK_ACCESS_FS_REFER). It may look like this would grant more access on disconnected files and directories, but the security policies are always enforced for all the evaluated hierarchies. This new behavior should be less surprising to users and safer from an access control perspective.
Remove a wrong WARN_ON_ONCE() canary in collect_domain_accesses() and fix the related comment.
Because opened files have their access rights stored in the related file security properties, there is no impact for disconnected or unlinked files.
--- security/landlock/errata/abi-1.h | 16 +++++++++++++++ security/landlock/fs.c | 40 +++++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 security/landlock/errata/abi-1.h
security/landlock/errata/abi-1.h new file +16 -0
@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/** +* DOC: erratum_3 +* +* Erratum 3: Disconnected directory handling +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* +* This fix addresses an issue with disconnected directories that occur when a +* directory is moved outside the scope of a bind mount. The change ensures +* that evaluated access rights include both those from the disconnected file +* hierarchy down to its filesystem root and those from the related mount point +* hierarchy. This prevents access right widening through rename or link +* actions. +*/ +LANDLOCK_ERRATUM (3 )
security/landlock/fs.c +28 -12
@@ -899,21 +899,31 @@ jump_up:break ; } } +if (unlikely (IS_ROOT (walker_path.dentry))) { -/* -* Stops at disconnected root directories. Only allows -* access to internal filesystems (e.g. nsfs, which is -* reachable through /proc/<pid>/ns/<namespace>). -*/ -if (walker_path.mnt->mnt_flags &MNT_INTERNAL ) { +if (likely (walker_path.mnt->mnt_flags &MNT_INTERNAL )) { +/* +* Stops and allows access when reaching disconnected root +* directories that are part of internal filesystems (e.g. nsfs, +* which is reachable through /proc/<pid>/ns/<namespace>). +*/ allowed_parent1 = true; allowed_parent2 = true; +break ; } -break ; + +/* +* We reached a disconnected root directory from a bind mount. +* Let's continue the walk with the mount point we missed. +*/ +dput (walker_path.dentry); + walker_path.dentry = walker_path.mnt->mnt_root; +dget (walker_path.dentry); + }else { + parent_dentry =dget_parent (walker_path.dentry); +dput (walker_path.dentry); + walker_path.dentry = parent_dentry; } - parent_dentry =dget_parent (walker_path.dentry); -dput (walker_path.dentry); - walker_path.dentry = parent_dentry; }path_put (&walker_path);
@@ -990,6 +1000,9 @@ static access_mask_t maybe_remove(const* file. While walking from @dir to @mnt_root, we record all the domain's * allowed accesses in @layer_masks_dom. * +* Because of disconnected directories, this walk may not reach @mnt_dir. In +* this case, the walk will continue to @mnt_dir after this call. +* * This is similar to is_access_to_paths_allowed() but much simpler because it * only handles walking on the same mount point and only checks one set of * accesses.
@@ -1031,8 +1044,11 @@ static bool collect_domain_accesses(break ; } -/* We should not reach a root other than @mnt_root. */ -if (dir == mnt_root ||WARN_ON_ONCE (IS_ROOT (dir))) +/* +* Stops at the mount point or the filesystem root for a disconnected +* directory. +*/ +if (dir == mnt_root ||unlikely (IS_ROOT (dir)))break ; parent_dentry =dget_parent (dir);
PATCH 1/2 selftests/landlock: Add tests for access through disconnected paths
From: Tingmao Wang <[email protected]>
[ Upstream commit a18ee3f31fd714173a62515d049d77e76ab55649 ]
This adds tests for the edge case discussed in [1], with specific ones for rename and link operations when the operands are through disconnected paths, as that go through a separate code path in Landlock.
This has resulted in a warning, due to collect_domain_accesses() not expecting to reach a different root from path->mnt:
# RUN layout1_bind.path_disconnected ...
# OK layout1_bind.path_disconnected
ok 96 layout1_bind.path_disconnected
# RUN layout1_bind.path_disconnected_rename ...
[..] ------------[ cut here ]------------
[..] WARNING: CPU: 3 PID: 385 at security/landlock/fs.c:1065 collect_domain_accesses
[..] ...
[..] RIP: 0010:collect_domain_accesses (security/landlock/fs.c:1065 (discriminator 2) security/landlock/fs.c:1031 (discriminator 2)) [..] current_check_refer_path (security/landlock/fs.c:1205)
[..] ...
[..] hook_path_rename (security/landlock/fs.c:1526)
[..] security_path_rename (security/security.c:2026 (discriminator 1))
[..] do_renameat2 (fs/namei.c:5264)
# OK layout1_bind.path_disconnected_rename
ok 97 layout1_bind.path_disconnected_rename
Move the const char definitions a bit above so that we can use the path for s4d1 in cleanup code.
--- tools/testing/selftests/landlock/fs_test.c | 423 ++++++++++++++++++++- 1 file changed, 415 insertions(+), 8 deletions(-)
tools/testing/selftests/landlock/fs_test.c +415 -8
@@ -4363,6 +4363,18 @@ TEST_F_FORK(ioctl, handle_file_access_file)FIXTURE (layout1_bind) {};/* clang-format on */ +static const char bind_dir_s1d3[] =TMP_DIR "/s2d1/s2d2/s1d3" ; +static const char bind_file1_s1d3[] =TMP_DIR "/s2d1/s2d2/s1d3/f1" ; + +/* Move targets for disconnected path tests. */ +static const char dir_s4d1[] =TMP_DIR "/s4d1" ; +static const char file1_s4d1[] =TMP_DIR "/s4d1/f1" ; +static const char file2_s4d1[] =TMP_DIR "/s4d1/f2" ; +static const char dir_s4d2[] =TMP_DIR "/s4d1/s4d2" ; +static const char file1_s4d2[] =TMP_DIR "/s4d1/s4d2/f1" ; +static const char file1_name[] ="f1" ; +static const char file2_name[] ="f2" ; +FIXTURE_SETUP (layout1_bind) {prepare_layout (_metadata);
@@ -4378,14 +4390,14 @@ FIXTURE_TEARDOWN_PARENT(layout1_bind) {/* umount(dir_s2d2)) is handled by namespace lifetime. */ +remove_path (file1_s4d1); +remove_path (file2_s4d1); +remove_layout1 (_metadata);cleanup_layout (_metadata); } -static const char bind_dir_s1d3[] =TMP_DIR "/s2d1/s2d2/s1d3" ; -static const char bind_file1_s1d3[] =TMP_DIR "/s2d1/s2d2/s1d3/f1" ; -/* * layout1_bind hierarchy: *
@@ -4396,20 +4408,25 @@ static const char bind_file1_s1d3[] = TMP_DIR "/s2d1/s2d2/s1d3/f1";* │ └── s1d2 * │ ├── f1 * │ ├── f2 -* │ └── s1d3 +* │ └── s1d3 [disconnected by path_disconnected] * │ ├── f1 * │ └── f2 * ├── s2d1 * │ ├── f1 -* │ └── s2d2 +* │ └── s2d2 [bind mount from s1d2] * │ ├── f1 * │ ├── f2 * │ └── s1d3 * │ ├── f1 * │ └── f2 -* └── s3d1 -* └── s3d2 -* └── s3d3 +* ├── s3d1 +* │ └── s3d2 +* │ └── s3d3 +* └── s4d1 [renamed from s1d3 by path_disconnected] +* ├── f1 +* ├── f2 +* └── s4d2 +* └── f1 */ TEST_F_FORK (layout1_bind, no_restriction)
@@ -4608,6 +4625,396 @@ TEST_F_FORK(layout1_bind, reparent_cross_mount)ASSERT_EQ (0 ,rename (bind_file1_s1d3, file1_s2d2)); } +/* +* Make sure access to file through a disconnected path works as expected. +* This test moves s1d3 to s4d1. +*/ +TEST_F_FORK(layout1_bind, path_disconnected) +{ +const struct rule layer1_allow_all[] = { + { + .path =TMP_DIR , + .access =ACCESS_ALL , + }, + {}, + }; +const struct rule layer2_allow_just_f1[] = { + { + .path = file1_s1d3, + .access =LANDLOCK_ACCESS_FS_READ_FILE , + }, + {}, + }; +const struct rule layer3_only_s1d2[] = { + { + .path = dir_s1d2, + .access =LANDLOCK_ACCESS_FS_READ_FILE , + }, + {}, + }; + +/* Landlock should not deny access just because it is disconnected. */ +int ruleset_fd_l1 = +create_ruleset (_metadata,ACCESS_ALL , layer1_allow_all); + +/* Creates the new ruleset now before we move the dir containing the file. */ +int ruleset_fd_l2 = +create_ruleset (_metadata,ACCESS_RW , layer2_allow_just_f1); +int ruleset_fd_l3 = +create_ruleset (_metadata,ACCESS_RW , layer3_only_s1d2); +int bind_s1d3_fd; + +ASSERT_LE (0 , ruleset_fd_l1); +ASSERT_LE (0 , ruleset_fd_l2); +ASSERT_LE (0 , ruleset_fd_l3); + +enforce_ruleset (_metadata, ruleset_fd_l1); +EXPECT_EQ (0 ,close (ruleset_fd_l1)); + + bind_s1d3_fd =open (bind_dir_s1d3,O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , bind_s1d3_fd); + +/* Tests access is possible before we move. */ +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd,".." ,O_RDONLY |O_DIRECTORY )); + +/* Makes it disconnected. */ + ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) + { +TH_LOG ("Failed to rename %s to %s: %s" , dir_s1d3, dir_s4d1, +strerror (errno)); + } + +/* Tests that access is still possible. */ +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); + +/* +* Tests that ".." is not possible (not because of Landlock, but just +* because it's disconnected). +*/ +EXPECT_EQ (ENOENT , +test_open_rel (bind_s1d3_fd,".." ,O_RDONLY |O_DIRECTORY )); + +/* This should still work with a narrower rule. */ +enforce_ruleset (_metadata, ruleset_fd_l2); +EXPECT_EQ (0 ,close (ruleset_fd_l2)); + +EXPECT_EQ (0 ,test_open (file1_s4d1,O_RDONLY )); +/* +* Accessing a file through a disconnected file descriptor can still be +* allowed by a rule tied to this file, even if it is no longer visible in +* its mount point. +*/ +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (EACCES ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); + +enforce_ruleset (_metadata, ruleset_fd_l3); +EXPECT_EQ (0 ,close (ruleset_fd_l3)); + +EXPECT_EQ (EACCES ,test_open (file1_s4d1,O_RDONLY )); +/* +* Accessing a file through a disconnected file descriptor can still be +* allowed by a rule tied to the original mount point, even if it is no +* longer visible in its mount point. +*/ +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (EACCES ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); +} + +/* +* Test that renameat with disconnected paths works under Landlock. This test +* moves s1d3 to s4d2, so that we can have a rule allowing refers on the move +* target's immediate parent. +*/ +TEST_F_FORK(layout1_bind, path_disconnected_rename) +{ +const struct rule layer1[] = { + { + .path = dir_s1d2, + .access =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_MAKE_DIR | +LANDLOCK_ACCESS_FS_REMOVE_DIR | +LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE | +LANDLOCK_ACCESS_FS_READ_FILE , + }, + { + .path = dir_s4d1, + .access =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_MAKE_DIR | +LANDLOCK_ACCESS_FS_REMOVE_DIR | +LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE | +LANDLOCK_ACCESS_FS_READ_FILE , + }, + {} + }; + +/* This layer only handles LANDLOCK_ACCESS_FS_READ_FILE. */ +const struct rule layer2_only_s1d2[] = { + { + .path = dir_s1d2, + .access =LANDLOCK_ACCESS_FS_READ_FILE , + }, + {}, + }; +int ruleset_fd_l1, ruleset_fd_l2; +pid_t child_pid; +int bind_s1d3_fd, status; + + ASSERT_EQ(0, mkdir(dir_s4d1, 0755)) + { +TH_LOG ("Failed to create %s: %s" , dir_s4d1,strerror (errno)); + } + ruleset_fd_l1 =create_ruleset (_metadata,ACCESS_ALL , layer1); + ruleset_fd_l2 =create_ruleset (_metadata,LANDLOCK_ACCESS_FS_READ_FILE , + layer2_only_s1d2); +ASSERT_LE (0 , ruleset_fd_l1); +ASSERT_LE (0 , ruleset_fd_l2); + +enforce_ruleset (_metadata, ruleset_fd_l1); +EXPECT_EQ (0 ,close (ruleset_fd_l1)); + + bind_s1d3_fd =open (bind_dir_s1d3,O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , bind_s1d3_fd); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); + +/* Tests ENOENT priority over EACCES for disconnected directory. */ +EXPECT_EQ (EACCES ,test_open_rel (bind_s1d3_fd,".." ,O_DIRECTORY )); + ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) + { +TH_LOG ("Failed to rename %s to %s: %s" , dir_s1d3, dir_s4d2, +strerror (errno)); + } +EXPECT_EQ (ENOENT ,test_open_rel (bind_s1d3_fd,".." ,O_DIRECTORY )); + +/* +* The file is no longer under s1d2 but we should still be able to access it +* with layer 2 because its mount point is evaluated as the first valid +* directory because it was initially a parent. Do a fork to test this so +* we don't prevent ourselves from renaming it back later. +*/ + child_pid =fork (); +ASSERT_LE (0 , child_pid); +if (child_pid ==0 ) { +enforce_ruleset (_metadata, ruleset_fd_l2); +EXPECT_EQ (0 ,close (ruleset_fd_l2)); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (EACCES ,test_open (file1_s4d2,O_RDONLY )); + +/* +* Tests that access widening checks indeed prevents us from renaming it +* back. +*/ +EXPECT_EQ (-1 ,rename (dir_s4d2, dir_s1d3)); +EXPECT_EQ (EXDEV , errno); + +/* +* Including through the now disconnected fd (but it should return +* EXDEV). +*/ +EXPECT_EQ (-1 ,renameat (bind_s1d3_fd, file1_name,AT_FDCWD , + file1_s2d2)); +EXPECT_EQ (EXDEV , errno); +_exit (_metadata->exit_code); +return ; + } + +EXPECT_EQ (child_pid,waitpid (child_pid, &status,0 )); +EXPECT_EQ (1 ,WIFEXITED (status)); +EXPECT_EQ (EXIT_SUCCESS ,WEXITSTATUS (status)); + + ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) + { +TH_LOG ("Failed to rename %s back to %s: %s" , dir_s4d1, dir_s1d3, +strerror (errno)); + } + +/* Now checks that we can access it under l2. */ + child_pid =fork (); +ASSERT_LE (0 , child_pid); +if (child_pid ==0 ) { +enforce_ruleset (_metadata, ruleset_fd_l2); +EXPECT_EQ (0 ,close (ruleset_fd_l2)); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open (file1_s1d3,O_RDONLY )); +_exit (_metadata->exit_code); +return ; + } + +EXPECT_EQ (child_pid,waitpid (child_pid, &status,0 )); +EXPECT_EQ (1 ,WIFEXITED (status)); +EXPECT_EQ (EXIT_SUCCESS ,WEXITSTATUS (status)); + +/* +* Also test that we can rename via a disconnected path. We move the +* dir back to the disconnected place first, then we rename file1 to +* file2 through our dir fd. +*/ + ASSERT_EQ(0, rename(dir_s1d3, dir_s4d2)) + { +TH_LOG ("Failed to rename %s to %s: %s" , dir_s1d3, dir_s4d2, +strerror (errno)); + } +ASSERT_EQ (0 , + renameat(bind_s1d3_fd, file1_name, bind_s1d3_fd, file2_name)) + { +TH_LOG ("Failed to rename %s to %s within disconnected %s: %s" , + file1_name, file2_name, bind_dir_s1d3,strerror (errno)); + } +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file2_name,O_RDONLY )); + ASSERT_EQ(0, renameat(bind_s1d3_fd, file2_name, AT_FDCWD, file1_s2d2)) + { +TH_LOG ("Failed to rename %s to %s through disconnected %s: %s" , + file2_name, file1_s2d2, bind_dir_s1d3,strerror (errno)); + } +EXPECT_EQ (0 ,test_open (file1_s2d2,O_RDONLY )); +EXPECT_EQ (0 ,test_open (file1_s1d2,O_RDONLY )); + +/* Move it back using the disconnected path as the target. */ + ASSERT_EQ(0, renameat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file1_name)) + { +TH_LOG ("Failed to rename %s to %s through disconnected %s: %s" , + file1_s1d2, file1_name, bind_dir_s1d3,strerror (errno)); + } + +/* Now make it connected again. */ + ASSERT_EQ(0, rename(dir_s4d2, dir_s1d3)) + { +TH_LOG ("Failed to rename %s back to %s: %s" , dir_s4d2, dir_s1d3, +strerror (errno)); + } + +/* Checks again that we can access it under l2. */ +enforce_ruleset (_metadata, ruleset_fd_l2); +EXPECT_EQ (0 ,close (ruleset_fd_l2)); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); +EXPECT_EQ (0 ,test_open (file1_s1d3,O_RDONLY )); +} + +/* +* Test that linkat(2) with disconnected paths works under Landlock. This +* test moves s1d3 to s4d1. +*/ +TEST_F_FORK(layout1_bind, path_disconnected_link) +{ +/* Ruleset to be applied after renaming s1d3 to s4d1. */ +const struct rule layer1[] = { + { + .path = dir_s4d1, + .access =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE , + }, + { + .path = dir_s2d2, + .access =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG | +LANDLOCK_ACCESS_FS_REMOVE_FILE , + }, + {} + }; +int ruleset_fd, bind_s1d3_fd; + +/* Removes unneeded files created by layout1, otherwise it will EEXIST. */ +ASSERT_EQ (0 ,unlink (file1_s1d2)); +ASSERT_EQ (0 ,unlink (file2_s1d3)); + + bind_s1d3_fd =open (bind_dir_s1d3,O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , bind_s1d3_fd); +EXPECT_EQ (0 ,test_open_rel (bind_s1d3_fd, file1_name,O_RDONLY )); + +/* Disconnects bind_s1d3_fd. */ + ASSERT_EQ(0, rename(dir_s1d3, dir_s4d1)) + { +TH_LOG ("Failed to rename %s to %s: %s" , dir_s1d3, dir_s4d1, +strerror (errno)); + } + +/* Need this later to test different parent link. */ + ASSERT_EQ(0, mkdir(dir_s4d2, 0755)) + { +TH_LOG ("Failed to create %s: %s" , dir_s4d2,strerror (errno)); + } + + ruleset_fd =create_ruleset (_metadata,ACCESS_ALL , layer1); +ASSERT_LE (0 , ruleset_fd); +enforce_ruleset (_metadata, ruleset_fd); +EXPECT_EQ (0 ,close (ruleset_fd)); + +/* From disconnected to connected. */ + ASSERT_EQ(0, linkat(bind_s1d3_fd, file1_name, AT_FDCWD, file1_s2d2, 0)) + { +TH_LOG ("Failed to link %s to %s via disconnected %s: %s" , + file1_name, file1_s2d2, bind_dir_s1d3,strerror (errno)); + } + +/* Tests that we can access via the new link... */ + EXPECT_EQ(0, test_open(file1_s2d2, O_RDONLY)) + { +TH_LOG ("Failed to open newly linked %s: %s" , file1_s2d2, +strerror (errno)); + } + +/* ...as well as the old one. */ + EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) + { +TH_LOG ("Failed to open original %s: %s" , file1_s4d1, +strerror (errno)); + } + +/* From connected to disconnected. */ +ASSERT_EQ (0 ,unlink (file1_s4d1)); + ASSERT_EQ(0, linkat(AT_FDCWD, file1_s2d2, bind_s1d3_fd, file2_name, 0)) + { +TH_LOG ("Failed to link %s to %s via disconnected %s: %s" , + file1_s2d2, file2_name, bind_dir_s1d3,strerror (errno)); + } +EXPECT_EQ (0 ,test_open (file2_s4d1,O_RDONLY )); +ASSERT_EQ (0 ,unlink (file1_s2d2)); + +/* From disconnected to disconnected (same parent). */ +ASSERT_EQ (0 , + linkat(bind_s1d3_fd, file2_name, bind_s1d3_fd, file1_name, 0)) + { +TH_LOG ("Failed to link %s to %s within disconnected %s: %s" , + file2_name, file1_name, bind_dir_s1d3,strerror (errno)); + } + EXPECT_EQ(0, test_open(file1_s4d1, O_RDONLY)) + { +TH_LOG ("Failed to open newly linked %s: %s" , file1_s4d1, +strerror (errno)); + } + EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, file1_name, O_RDONLY)) + { +TH_LOG ("Failed to open %s through newly created link under disconnected path: %s" , + file1_name,strerror (errno)); + } +ASSERT_EQ (0 ,unlink (file2_s4d1)); + +/* From disconnected to disconnected (different parent). */ +ASSERT_EQ (0 , + linkat(bind_s1d3_fd, file1_name, bind_s1d3_fd, "s4d2/f1", 0)) + { +TH_LOG ("Failed to link %s to %s within disconnected %s: %s" , + file1_name,"s4d2/f1" , bind_dir_s1d3,strerror (errno)); + } + EXPECT_EQ(0, test_open(file1_s4d2, O_RDONLY)) + { +TH_LOG ("Failed to open %s after link: %s" , file1_s4d2, +strerror (errno)); + } + EXPECT_EQ(0, test_open_rel(bind_s1d3_fd, "s4d2/f1", O_RDONLY)) + { +TH_LOG ("Failed to open %s through disconnected path after link: %s" , +"s4d2/f1" ,strerror (errno)); + } +} +#define LOWER_BASE TMP_DIR "/lower"#define LOWER_DATA LOWER_BASE "/data"static const char lower_fl1[] =LOWER_DATA "/fl1" ;
Re: [PATCH 6.12.y 1/2] selftests/landlock: Add tests for access through disconnected paths
Quoted 5 lines
[ Upstream commit
a18ee3f31fd714173a62515d049d77e76ab55649]This adds tests for the edge case discussed in [1], with specific ones for rename and link operations when the operands are through disconnected paths, as that go through a separate code path in Landlock.
Queued for 6.18, 6.12, 6.6 and 6.1, thanks.
Signature · 2 lines
Thanks, Sasha
PATCH 2/2 selftests/landlock: Add disconnected leafs and branch test suites
In reply to Greg Kroah-Hartman[ Upstream commit 54f9baf537b0a091adad860ec92e3e18e0a0754c ]
Test disconnected directories with two test suites (layout4_disconnected_leafs and layout5_disconnected_branch) and 43 variants to cover the main corner cases.
These tests are complementary to the previous commit.
Add test_renameat() and test_exchangeat() helpers.
Test coverage for security/landlock is 92.1% of 1927 lines according to LLVM 20.
--- tools/testing/selftests/landlock/fs_test.c | 1051 ++++++++++++++++++++ 1 file changed, 1051 insertions(+)
tools/testing/selftests/landlock/fs_test.c +1051 -0
@@ -2109,6 +2109,22 @@ static int test_exchange(const char *const oldpath, const char *const newpath)return 0 ; } +static int test_renameat (int olddirfd,const char *oldpath,int newdirfd, +const char *newpath) +{ +if (renameat2 (olddirfd, oldpath, newdirfd, newpath,0 )) +return errno; +return 0 ; +} + +static int test_exchangeat (int olddirfd,const char *oldpath,int newdirfd, +const char *newpath) +{ +if (renameat2 (olddirfd, oldpath, newdirfd, newpath,RENAME_EXCHANGE )) +return errno; +return 0 ; +} + TEST_F_FORK(layout1, rename_file) {const struct rule rules[] = {
@@ -5015,6 +5031,1041 @@ TEST_F_FORK(layout1_bind, path_disconnected_link) } } +/* +* layout4_disconnected_leafs with bind mount and renames: +* +* tmp +* ├── s1d1 +* │ └── s1d2 [source of the bind mount] +* │ ├── s1d31 +* │ │ └── s1d41 [now renamed beneath s3d1] +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d32 +* │ └── s1d42 [now renamed beneath s4d1] +* │ ├── f3 +* │ └── f4 +* ├── s2d1 +* │ └── s2d2 [bind mount of s1d2] +* │ ├── s1d31 +* │ │ └── s1d41 [opened FD, now renamed beneath s3d1] +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d32 +* │ └── s1d42 [opened FD, now renamed beneath s4d1] +* │ ├── f3 +* │ └── f4 +* ├── s3d1 +* │ └── s1d41 [renamed here] +* │ ├── f1 +* │ └── f2 +* └── s4d1 +* └── s1d42 [renamed here] +* ├── f3 +* └── f4 +*/ +/* clang-format off */ +FIXTURE (layout4_disconnected_leafs) { +int s2d2_fd; +}; +/* clang-format on */ + +FIXTURE_SETUP (layout4_disconnected_leafs) +{ +prepare_layout (_metadata); + +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f4" ); +create_directory (_metadata,TMP_DIR "/s2d1/s2d2" ); +create_directory (_metadata,TMP_DIR "/s3d1" ); +create_directory (_metadata,TMP_DIR "/s4d1" ); + + self->s2d2_fd = +open (TMP_DIR "/s2d1/s2d2" ,O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , self->s2d2_fd); + +set_cap (_metadata,CAP_SYS_ADMIN ); +ASSERT_EQ (0 ,mount (TMP_DIR "/s1d1/s1d2" ,TMP_DIR "/s2d1/s2d2" ,NULL , +MS_BIND ,NULL )); +clear_cap (_metadata,CAP_SYS_ADMIN ); +} + +FIXTURE_TEARDOWN_PARENT (layout4_disconnected_leafs) +{ +/* umount(TMP_DIR "/s2d1") is handled by namespace lifetime. */ + +/* Removes files after renames. */ +remove_path (TMP_DIR "/s3d1/s1d41/f1" ); +remove_path (TMP_DIR "/s3d1/s1d41/f2" ); +remove_path (TMP_DIR "/s4d1/s1d42/f1" ); +remove_path (TMP_DIR "/s4d1/s1d42/f3" ); +remove_path (TMP_DIR "/s4d1/s1d42/f4" ); +remove_path (TMP_DIR "/s4d1/s1d42/f5" ); + +cleanup_layout (_metadata); +} + +FIXTURE_VARIANT (layout4_disconnected_leafs) +{ +/* +* Parent of the bind mount source. It should always be ignored when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s1d1; +/* +* Source of bind mount (to s2d2). It should always be enforced when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s1d2; +/* +* Original parent of s1d41. It should always be ignored when testing +* against files under the s1d41 disconnected directory. +*/ +const __u64 allowed_s1d31; +/* +* Original parent of s1d42. It should always be ignored when testing +* against files under the s1d42 disconnected directory. +*/ +const __u64 allowed_s1d32; +/* +* Opened and disconnected source directory. It should always be enforced +* when testing against files under the s1d41 disconnected directory. +*/ +const __u64 allowed_s1d41; +/* +* Opened and disconnected source directory. It should always be enforced +* when testing against files under the s1d42 disconnected directory. +*/ +const __u64 allowed_s1d42; +/* +* File in the s1d41 disconnected directory. It should always be enforced +* when testing against itself under the s1d41 disconnected directory. +*/ +const __u64 allowed_f1; +/* +* File in the s1d41 disconnected directory. It should always be enforced +* when testing against itself under the s1d41 disconnected directory. +*/ +const __u64 allowed_f2; +/* +* File in the s1d42 disconnected directory. It should always be enforced +* when testing against itself under the s1d42 disconnected directory. +*/ +const __u64 allowed_f3; +/* +* Parent of the bind mount destination. It should always be enforced when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s2d1; +/* +* Directory covered by the bind mount. It should always be ignored when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s2d2; +/* +* New parent of the renamed s1d41. It should always be ignored when +* testing against files under the s1d41 disconnected directory. +*/ +const __u64 allowed_s3d1; +/* +* New parent of the renamed s1d42. It should always be ignored when +* testing against files under the s1d42 disconnected directory. +*/ +const __u64 allowed_s4d1; + +/* Expected result of the call to open([fd:s1d41]/f1, O_RDONLY). */ +const int expected_read_result; +/* Expected result of the call to renameat([fd:s1d41]/f1, [fd:s1d42]/f1). */ +const int expected_rename_result; +/* +* Expected result of the call to renameat([fd:s1d41]/f2, [fd:s1d42]/f3, +* RENAME_EXCHANGE). +*/ +const int expected_exchange_result; +/* Expected result of the call to renameat([fd:s1d42]/f4, [fd:s1d42]/f5). */ +const int expected_same_dir_rename_result; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d1_mount_src_parent) { +/* clang-format on */ + .allowed_s1d1 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result =EACCES , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_refer) { +/* clang-format on */ + .allowed_s1d2 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_READ_FILE , + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_create) { +/* clang-format on */ + .allowed_s1d2 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result =EXDEV , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d2_mount_src_rename) { +/* clang-format on */ + .allowed_s1d2 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG , + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d31_s1d32_old_parent) { +/* clang-format on */ + .allowed_s1d31 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .allowed_s1d32 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result =EACCES , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_refer) { +/* clang-format on */ + .allowed_s1d41 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE , + .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE , + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_create) { +/* clang-format on */ + .allowed_s1d41 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .allowed_s1d42 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result =EXDEV , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_even) { +/* clang-format on */ + .allowed_s1d41 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG , + .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* The destination directory has more access right. */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_more) { +/* clang-format on */ + .allowed_s1d41 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .allowed_s1d42 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_EXECUTE, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, +/* Access denied. */ + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* The destination directory has less access right. */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s1d41_s1d42_disconnected_rename_less) { +/* clang-format on */ + .allowed_s1d41 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_EXECUTE, + .allowed_s1d42 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, +/* Access allowed. */ + .expected_rename_result = 0, + .expected_exchange_result =EXDEV , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_create) { +/* clang-format on */ + .allowed_s2d1 =LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG , + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_refer) { +/* clang-format on */ + .allowed_s2d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result =EACCES , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d1_mount_dst_parent_mini) { +/* clang-format on */ + .allowed_s2d1 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG , + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s2d2_covered_by_mount) { +/* clang-format on */ + .allowed_s2d2 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* Tests collect_domain_accesses(). */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_refer) { +/* clang-format on */ + .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result =EACCES , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_new_parent_create) { +/* clang-format on */ + .allowed_s3d1 =LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG , + .allowed_s4d1 = LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_MAKE_REG , + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +FIXTURE_VARIANT_ADD (layout4_disconnected_leafs, + s3d1_s4d1_disconnected_rename_even){ +/* clang-format on */ + .allowed_s3d1 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG , + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* The destination directory has more access right. */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_more) { +/* clang-format on */ + .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .allowed_s4d1 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_EXECUTE, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, +/* Access denied. */ + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* The destination directory has less access right. */ +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, s3d1_s4d1_disconnected_rename_less) { +/* clang-format on */ + .allowed_s3d1 =LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_MAKE_REG | + LANDLOCK_ACCESS_FS_EXECUTE, + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, +/* Access allowed. */ + .expected_rename_result = 0, + .expected_exchange_result =EXDEV , +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout4_disconnected_leafs, f1_f2_f3) { +/* clang-format on */ + .allowed_f1 =LANDLOCK_ACCESS_FS_READ_FILE , + .allowed_f2 = LANDLOCK_ACCESS_FS_READ_FILE, + .allowed_f3 = LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +TEST_F_FORK(layout4_disconnected_leafs, read_rename_exchange) +{ + const __u64 handled_access = +LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE |LANDLOCK_ACCESS_FS_MAKE_REG ; +const struct rule rules[] = { + { + .path =TMP_DIR "/s1d1" , + .access = variant->allowed_s1d1, + }, + { + .path =TMP_DIR "/s1d1/s1d2" , + .access = variant->allowed_s1d2, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d31" , + .access = variant->allowed_s1d31, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d32" , + .access = variant->allowed_s1d32, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d31/s1d41" , + .access = variant->allowed_s1d41, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d32/s1d42" , + .access = variant->allowed_s1d42, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f1" , + .access = variant->allowed_f1, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d31/s1d41/f2" , + .access = variant->allowed_f2, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d32/s1d42/f3" , + .access = variant->allowed_f3, + }, + { + .path =TMP_DIR "/s2d1" , + .access = variant->allowed_s2d1, + }, +/* s2d2_fd */ + { + .path =TMP_DIR "/s3d1" , + .access = variant->allowed_s3d1, + }, + { + .path =TMP_DIR "/s4d1" , + .access = variant->allowed_s4d1, + }, + {}, + }; +int ruleset_fd, s1d41_bind_fd, s1d42_bind_fd; + + ruleset_fd =create_ruleset (_metadata, handled_access, rules); +ASSERT_LE (0 , ruleset_fd); + +/* Adds rule for the covered directory. */ +if (variant->allowed_s2d2) { +ASSERT_EQ (0 ,landlock_add_rule ( + ruleset_fd,LANDLOCK_RULE_PATH_BENEATH , + &(struct landlock_path_beneath_attr ){ + .parent_fd = self->s2d2_fd, + .allowed_access = + variant->allowed_s2d2, + }, +0 )); + } +EXPECT_EQ (0 ,close (self->s2d2_fd)); + + s1d41_bind_fd =open (TMP_DIR "/s2d1/s2d2/s1d31/s1d41" , +O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , s1d41_bind_fd); + s1d42_bind_fd =open (TMP_DIR "/s2d1/s2d2/s1d32/s1d42" , +O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , s1d42_bind_fd); + +/* Disconnects and checks source and destination directories. */ +EXPECT_EQ (0 ,test_open_rel (s1d41_bind_fd,".." ,O_DIRECTORY )); +EXPECT_EQ (0 ,test_open_rel (s1d42_bind_fd,".." ,O_DIRECTORY )); +/* Renames to make it accessible through s3d1/s1d41 */ +ASSERT_EQ (0 ,test_renameat (AT_FDCWD ,TMP_DIR "/s1d1/s1d2/s1d31/s1d41" , +AT_FDCWD ,TMP_DIR "/s3d1/s1d41" )); +/* Renames to make it accessible through s4d1/s1d42 */ +ASSERT_EQ (0 ,test_renameat (AT_FDCWD ,TMP_DIR "/s1d1/s1d2/s1d32/s1d42" , +AT_FDCWD ,TMP_DIR "/s4d1/s1d42" )); +EXPECT_EQ (ENOENT ,test_open_rel (s1d41_bind_fd,".." ,O_DIRECTORY )); +EXPECT_EQ (ENOENT ,test_open_rel (s1d42_bind_fd,".." ,O_DIRECTORY )); + +enforce_ruleset (_metadata, ruleset_fd); +EXPECT_EQ (0 ,close (ruleset_fd)); + +EXPECT_EQ (variant->expected_read_result, +test_open_rel (s1d41_bind_fd,"f1" ,O_RDONLY )); + +EXPECT_EQ (variant->expected_rename_result, +test_renameat (s1d41_bind_fd,"f1" , s1d42_bind_fd,"f1" )); +EXPECT_EQ (variant->expected_exchange_result, +test_exchangeat (s1d41_bind_fd,"f2" , s1d42_bind_fd,"f3" )); + +EXPECT_EQ (variant->expected_same_dir_rename_result, +test_renameat (s1d42_bind_fd,"f4" , s1d42_bind_fd,"f5" )); +} + +/* +* layout5_disconnected_branch before rename: +* +* tmp +* ├── s1d1 +* │ └── s1d2 [source of the first bind mount] +* │ └── s1d3 +* │ ├── s1d41 +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d42 +* │ ├── f3 +* │ └── f4 +* ├── s2d1 +* │ └── s2d2 [source of the second bind mount] +* │ └── s2d3 +* │ └── s2d4 [first s1d2 bind mount] +* │ └── s1d3 +* │ ├── s1d41 +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d42 +* │ ├── f3 +* │ └── f4 +* ├── s3d1 +* │ └── s3d2 [second s2d2 bind mount] +* │ └── s2d3 +* │ └── s2d4 [first s1d2 bind mount] +* │ └── s1d3 +* │ ├── s1d41 +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d42 +* │ ├── f3 +* │ └── f4 +* └── s4d1 +* +* After rename: +* +* tmp +* ├── s1d1 +* │ └── s1d2 [source of the first bind mount] +* │ └── s1d3 +* │ ├── s1d41 +* │ │ ├── f1 +* │ │ └── f2 +* │ └── s1d42 +* │ ├── f3 +* │ └── f4 +* ├── s2d1 +* │ └── s2d2 [source of the second bind mount] +* ├── s3d1 +* │ └── s3d2 [second s2d2 bind mount] +* └── s4d1 +* └── s2d3 [renamed here] +* └── s2d4 [first s1d2 bind mount] +* └── s1d3 +* ├── s1d41 +* │ ├── f1 +* │ └── f2 +* └── s1d42 +* ├── f3 +* └── f4 +* +* Decision path for access from the s3d1/s3d2/s2d3/s2d4/s1d3 file descriptor: +* 1. first bind mount: s1d3 -> s1d2 +* 2. second bind mount: s2d3 +* 3. tmp mount: s4d1 -> tmp [disconnected branch] +* 4. second bind mount: s2d2 +* 5. tmp mount: s3d1 -> tmp +* 6. parent mounts: [...] -> / +* +* The s4d1 directory is evaluated even if it is not in the s2d2 mount. +*/ + +/* clang-format off */ +FIXTURE (layout5_disconnected_branch) { +int s2d4_fd, s3d2_fd; +}; +/* clang-format on */ + +FIXTURE_SETUP (layout5_disconnected_branch) +{ +prepare_layout (_metadata); + +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3" ); +create_file (_metadata,TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4" ); +create_directory (_metadata,TMP_DIR "/s2d1/s2d2/s2d3/s2d4" ); +create_directory (_metadata,TMP_DIR "/s3d1/s3d2" ); +create_directory (_metadata,TMP_DIR "/s4d1" ); + + self->s2d4_fd =open (TMP_DIR "/s2d1/s2d2/s2d3/s2d4" , +O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , self->s2d4_fd); + + self->s3d2_fd = +open (TMP_DIR "/s3d1/s3d2" ,O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , self->s3d2_fd); + +set_cap (_metadata,CAP_SYS_ADMIN ); +ASSERT_EQ (0 ,mount (TMP_DIR "/s1d1/s1d2" ,TMP_DIR "/s2d1/s2d2/s2d3/s2d4" , +NULL ,MS_BIND ,NULL )); +ASSERT_EQ (0 ,mount (TMP_DIR "/s2d1/s2d2" ,TMP_DIR "/s3d1/s3d2" ,NULL , +MS_BIND |MS_REC ,NULL )); +clear_cap (_metadata,CAP_SYS_ADMIN ); +} + +FIXTURE_TEARDOWN_PARENT (layout5_disconnected_branch) +{ +/* Bind mounts are handled by namespace lifetime. */ + +/* Removes files after renames. */ +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f1" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d41/f2" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f1" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f3" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f4" ); +remove_path (TMP_DIR "/s1d1/s1d2/s1d3/s1d42/f5" ); + +cleanup_layout (_metadata); +} + +FIXTURE_VARIANT (layout5_disconnected_branch) +{ +/* +* Parent of all files. It should always be enforced when testing against +* files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_base; +/* +* Parent of the first bind mount source. It should always be ignored when +* testing against files under the s1d41 or s1d42 disconnected directories. +*/ +const __u64 allowed_s1d1; +const __u64 allowed_s1d2; +const __u64 allowed_s1d3; +const __u64 allowed_s2d1; +const __u64 allowed_s2d2; +const __u64 allowed_s2d3; +const __u64 allowed_s2d4; +const __u64 allowed_s3d1; +const __u64 allowed_s3d2; +const __u64 allowed_s4d1; + +/* Expected result of the call to open([fd:s1d3]/s1d41/f1, O_RDONLY). */ +const int expected_read_result; +/* +* Expected result of the call to renameat([fd:s1d3]/s1d41/f1, +* [fd:s1d3]/s1d42/f1). +*/ +const int expected_rename_result; +/* +* Expected result of the call to renameat([fd:s1d3]/s1d41/f2, +* [fd:s1d3]/s1d42/f3, RENAME_EXCHANGE). +*/ +const int expected_exchange_result; +/* +* Expected result of the call to renameat([fd:s1d3]/s1d42/f4, +* [fd:s1d3]/s1d42/f5). +*/ +const int expected_same_dir_rename_result; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d1_mount1_src_parent) { +/* clang-format on */ + .allowed_s1d1 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_refer) { +/* clang-format on */ + .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_create) { +/* clang-format on */ + .allowed_s1d2 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d2_mount1_src_rename) { +/* clang-format on */ + .allowed_s1d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_refer) { +/* clang-format on */ + .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_create) { +/* clang-format on */ + .allowed_s1d3 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_rename) { +/* clang-format on */ + .allowed_s1d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s1d3_fd_full) { +/* clang-format on */ + .allowed_s1d3 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d1_mount2_src_parent) { +/* clang-format on */ + .allowed_s2d1 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_refer) { +/* clang-format on */ + .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_create) { +/* clang-format on */ + .allowed_s2d2 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d2_mount2_src_rename) { +/* clang-format on */ + .allowed_s2d2 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_refer) { +/* clang-format on */ + .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_create) { +/* clang-format on */ + .allowed_s2d3 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d3_mount1_dst_parent_rename) { +/* clang-format on */ + .allowed_s2d3 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s2d4_mount1_dst) { +/* clang-format on */ + .allowed_s2d4 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_refer) { +/* clang-format on */ + .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_create) { +/* clang-format on */ + .allowed_s3d1 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d1_mount2_dst_parent_rename) { +/* clang-format on */ + .allowed_s3d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s3d2_mount1_dst) { +/* clang-format on */ + .allowed_s3d2 =LANDLOCK_ACCESS_FS_REFER | +LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_refer) { +/* clang-format on */ + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_READ_FILE, + .expected_read_result = 0, + .expected_same_dir_rename_result = EACCES, + .expected_rename_result = EACCES, + .expected_exchange_result = EACCES, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_create) { +/* clang-format on */ + .allowed_s4d1 =LANDLOCK_ACCESS_FS_READ_FILE | + LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = 0, + .expected_same_dir_rename_result = 0, + .expected_rename_result = EXDEV, + .expected_exchange_result = EXDEV, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout5_disconnected_branch, s4d1_rename_parent_rename) { +/* clang-format on */ + .allowed_s4d1 = LANDLOCK_ACCESS_FS_REFER | LANDLOCK_ACCESS_FS_MAKE_REG, + .expected_read_result = EACCES, + .expected_same_dir_rename_result = 0, + .expected_rename_result = 0, + .expected_exchange_result = 0, +}; + +TEST_F_FORK(layout5_disconnected_branch, read_rename_exchange) +{ +const __u64 handled_access = +LANDLOCK_ACCESS_FS_REFER |LANDLOCK_ACCESS_FS_READ_FILE | +LANDLOCK_ACCESS_FS_EXECUTE |LANDLOCK_ACCESS_FS_MAKE_REG ; +const struct rule rules[] = { + { + .path =TMP_DIR "/s1d1" , + .access = variant->allowed_s1d1, + }, + { + .path =TMP_DIR "/s1d1/s1d2" , + .access = variant->allowed_s1d2, + }, + { + .path =TMP_DIR "/s1d1/s1d2/s1d3" , + .access = variant->allowed_s1d3, + }, + { + .path =TMP_DIR "/s2d1" , + .access = variant->allowed_s2d1, + }, + { + .path =TMP_DIR "/s2d1/s2d2" , + .access = variant->allowed_s2d2, + }, + { + .path =TMP_DIR "/s2d1/s2d2/s2d3" , + .access = variant->allowed_s2d3, + }, +/* s2d4_fd */ + { + .path =TMP_DIR "/s3d1" , + .access = variant->allowed_s3d1, + }, +/* s3d2_fd */ + { + .path =TMP_DIR "/s4d1" , + .access = variant->allowed_s4d1, + }, + {}, + }; +int ruleset_fd, s1d3_bind_fd; + + ruleset_fd =create_ruleset (_metadata, handled_access, rules); +ASSERT_LE (0 , ruleset_fd); + +/* Adds rules for the covered directories. */ +if (variant->allowed_s2d4) { +ASSERT_EQ (0 ,landlock_add_rule ( + ruleset_fd,LANDLOCK_RULE_PATH_BENEATH , + &(struct landlock_path_beneath_attr ){ + .parent_fd = self->s2d4_fd, + .allowed_access = + variant->allowed_s2d4, + }, +0 )); + } +EXPECT_EQ (0 ,close (self->s2d4_fd)); + +if (variant->allowed_s3d2) { +ASSERT_EQ (0 ,landlock_add_rule ( + ruleset_fd,LANDLOCK_RULE_PATH_BENEATH , + &(struct landlock_path_beneath_attr ){ + .parent_fd = self->s3d2_fd, + .allowed_access = + variant->allowed_s3d2, + }, +0 )); + } +EXPECT_EQ (0 ,close (self->s3d2_fd)); + + s1d3_bind_fd =open (TMP_DIR "/s3d1/s3d2/s2d3/s2d4/s1d3" , +O_DIRECTORY |O_PATH |O_CLOEXEC ); +ASSERT_LE (0 , s1d3_bind_fd); + +/* Disconnects and checks source and destination directories. */ +EXPECT_EQ (0 ,test_open_rel (s1d3_bind_fd,".." ,O_DIRECTORY )); +EXPECT_EQ (0 ,test_open_rel (s1d3_bind_fd,"../.." ,O_DIRECTORY )); +/* Renames to make it accessible through s3d1/s1d41 */ +ASSERT_EQ (0 ,test_renameat (AT_FDCWD ,TMP_DIR "/s2d1/s2d2/s2d3" , +AT_FDCWD ,TMP_DIR "/s4d1/s2d3" )); +EXPECT_EQ (0 ,test_open_rel (s1d3_bind_fd,".." ,O_DIRECTORY )); +EXPECT_EQ (ENOENT ,test_open_rel (s1d3_bind_fd,"../.." ,O_DIRECTORY )); + +enforce_ruleset (_metadata, ruleset_fd); +EXPECT_EQ (0 ,close (ruleset_fd)); + +EXPECT_EQ (variant->expected_read_result, +test_open_rel (s1d3_bind_fd,"s1d41/f1" ,O_RDONLY )); + +EXPECT_EQ (variant->expected_rename_result, +test_renameat (s1d3_bind_fd,"s1d41/f1" , s1d3_bind_fd, +"s1d42/f1" )); +EXPECT_EQ (variant->expected_exchange_result, +test_exchangeat (s1d3_bind_fd,"s1d41/f2" , s1d3_bind_fd, +"s1d42/f3" )); + +EXPECT_EQ (variant->expected_same_dir_rename_result, +test_renameat (s1d3_bind_fd,"s1d42/f4" , s1d3_bind_fd, +"s1d42/f5" )); +} +#define LOWER_BASE TMP_DIR "/lower"#define LOWER_DATA LOWER_BASE "/data"static const char lower_fl1[] =LOWER_DATA "/fl1" ;
Greg Kroah-Hartman
maintainerPATCH 217/244 ice: fix using untrusted value of pkt_len in ice_vc_fdir_parse_raw()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mateusz Polchlopek <[email protected]>
[ Upstream commit 1388dd564183a5a18ec4a966748037736b5653c5 ]
Fix using the untrusted value of proto->raw.pkt_len in function ice_vc_fdir_parse_raw() by verifying if it does not exceed the VIRTCHNL_MAX_SIZE_RAW_PACKET value.
--- drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c | 24 +++++++++++++-------- 1 file changed, 15 insertions(+), 9 deletions(-)
drivers/net/ethernet/intel/ice/ice_virtchnl_fdir.c +15 -9
@@ -832,21 +832,27 @@ ice_vc_fdir_parse_raw(struct ice_vf *vf,struct virtchnl_proto_hdrs *proto, struct virtchnl_fdir_fltr_conf *conf) { - u8 *pkt_buf, *msk_buf __free(kfree); + u8 *pkt_buf, *msk_buf __free(kfree) = NULL;struct ice_parser_result rslt;struct ice_pf *pf = vf->pf; +u16 pkt_len, udp_port =0 ;struct ice_parser *psr;int status = -ENOMEM ;struct ice_hw *hw; -u16 udp_port =0 ; - pkt_buf =kzalloc (proto->raw.pkt_len,GFP_KERNEL ); - msk_buf =kzalloc (proto->raw.pkt_len,GFP_KERNEL ); + pkt_len = proto->raw.pkt_len; + +if (!pkt_len || pkt_len >VIRTCHNL_MAX_SIZE_RAW_PACKET ) +return -EINVAL ; + + pkt_buf =kzalloc (pkt_len,GFP_KERNEL ); + msk_buf =kzalloc (pkt_len,GFP_KERNEL ); +if (!pkt_buf || !msk_buf) goto err_mem_alloc; -memcpy (pkt_buf, proto->raw.spec, proto->raw.pkt_len); -memcpy (msk_buf, proto->raw.mask, proto->raw.pkt_len); +memcpy (pkt_buf, proto->raw.spec, pkt_len); +memcpy (msk_buf, proto->raw.mask, pkt_len); hw = &pf->hw;
@@ -862,7 +868,7 @@ ice_vc_fdir_parse_raw(struct ice_vf *vf,if (ice_get_open_tunnel_port (hw, &udp_port,TNL_VXLAN ))ice_parser_vxlan_tunnel_set (psr, udp_port, true); - status =ice_parser_run (psr, pkt_buf, proto->raw.pkt_len, &rslt); + status =ice_parser_run (psr, pkt_buf, pkt_len, &rslt);if (status) goto err_parser_destroy;
@@ -876,7 +882,7 @@ ice_vc_fdir_parse_raw(struct ice_vf *vf, } status =ice_parser_profile_init (&rslt, pkt_buf, msk_buf, - proto->raw.pkt_len,ICE_BLK_FD , + pkt_len,ICE_BLK_FD , conf->prof);if (status) goto err_parser_profile_init;
@@ -885,7 +891,7 @@ ice_vc_fdir_parse_raw(struct ice_vf *vf,ice_parser_profile_dump (hw, conf->prof);/* Store raw flow info into @conf */ - conf->pkt_len = proto->raw.pkt_len; + conf->pkt_len = pkt_len; conf->pkt_buf = pkt_buf; conf->parser_ena = true;
Greg Kroah-Hartman
maintainerPATCH 218/244 ice: Fix PTP NULL pointer dereference during VSI rebuild
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aaron Ma <[email protected]>
[ Upstream commit fc6f36eaaedcf4b81af6fe1a568f018ffd530660 ]
Fix race condition where PTP periodic work runs while VSI is being rebuilt, accessing NULL vsi->rx_rings.
The sequence was:
1. ice_ptp_prepare_for_reset() cancels PTP work
2. ice_ptp_rebuild() immediately queues PTP work
3. VSI rebuild happens AFTER ice_ptp_rebuild()
4. PTP work runs and accesses NULL vsi->rx_rings
Fix: Keep PTP work cancelled during rebuild, only queue it after VSI rebuild completes in ice_rebuild().
Added ice_ptp_queue_work() helper function to encapsulate the logic for queuing PTP work, ensuring it's only queued when PTP is supported and the state is ICE_PTP_READY.
Error log:
[ 121.392544] ice 0000:60:00.1: PTP reset successful [ 121.392692] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 121.392712] #PF: supervisor read access in kernel mode [ 121.392720] #PF: error_code(0x0000) - not-present page [ 121.392727] PGD 0 [ 121.392734] Oops: Oops: 0000 [#1] SMP NOPTI [ 121.392746] CPU: 8 UID: 0 PID: 1005 Comm: ice-ptp-0000:60 Tainted: G S 6.19.0-rc6+ #4 PREEMPT(voluntary) [ 121.392761] Tainted: [S]=CPU_OUT_OF_SPEC [ 121.392773] RIP: 0010:ice_ptp_update_cached_phctime+0xbf/0x150 [ice] [ 121.393042] Call Trace: [ 121.393047] <TASK> [ 121.393055] ice_ptp_periodic_work+0x69/0x180 [ice] [ 121.393202] kthread_worker_fn+0xa2/0x260 [ 121.393216] ? __pfx_ice_ptp_periodic_work+0x10/0x10 [ice] [ 121.393359] ? __pfx_kthread_worker_fn+0x10/0x10 [ 121.393371] kthread+0x10d/0x230 [ 121.393382] ? __pfx_kthread+0x10/0x10 [ 121.393393] ret_from_fork+0x273/0x2b0 [ 121.393407] ? __pfx_kthread+0x10/0x10 [ 121.393417] ret_from_fork_asm+0x1a/0x30 [ 121.393432] </TASK>
[Harshit: Backported to 6.12.y, ice_ptp_prepare_rebuild_sec() is not present in 6.12.y]
--- drivers/net/ethernet/intel/ice/ice_main.c | 3 +++ drivers/net/ethernet/intel/ice/ice_ptp.c | 17 ++++++++++++++--- drivers/net/ethernet/intel/ice/ice_ptp.h | 5 +++++ 3 files changed, 22 insertions(+), 3 deletions(-)
drivers/net/ethernet/intel/ice/ice_main.c +3 -0
@@ -7792,6 +7792,9 @@ static void ice_rebuild(struct ice_pf *p/* Restore timestamp mode settings after VSI rebuild */ ice_ptp_restore_timestamp_mode (pf); + +/* Start PTP periodic work after VSI is fully rebuilt */ +ice_ptp_queue_work (pf);return ; err_vsi_rebuild:
drivers/net/ethernet/intel/ice/ice_ptp.c +14 -3
@@ -2754,6 +2754,20 @@ static void ice_ptp_periodic_work(struct }/** +* ice_ptp_queue_work - Queue PTP periodic work for a PF +* @pf: Board private structure +* +* Helper function to queue PTP periodic work after VSI rebuild completes. +* This ensures that PTP work only runs when VSI structures are ready. +*/ +void ice_ptp_queue_work (struct ice_pf *pf) +{ +if (test_bit (ICE_FLAG_PTP_SUPPORTED , pf->flags) && + pf->ptp.state ==ICE_PTP_READY ) +kthread_queue_delayed_work (pf->ptp.kworker, &pf->ptp.work,0 ); +} + +/** * ice_ptp_prepare_for_reset - Prepare PTP for reset * @pf: Board private structure * @reset_type: the reset type being performed
@@ -2888,9 +2902,6 @@ void ice_ptp_rebuild(struct ice_pf *pf, ptp->state =ICE_PTP_READY ; -/* Start periodic work going */ -kthread_queue_delayed_work (ptp->kworker, &ptp->work,0 ); -dev_info (ice_pf_to_dev (pf),"PTP reset successful\n" );return ;
drivers/net/ethernet/intel/ice/ice_ptp.h +5 -0
@@ -333,6 +333,7 @@ void ice_ptp_prepare_for_reset(struct icvoid ice_ptp_init (struct ice_pf *pf);void ice_ptp_release (struct ice_pf *pf);void ice_ptp_link_change (struct ice_pf *pf,bool linkup); +void ice_ptp_queue_work (struct ice_pf *pf);#else /* IS_ENABLED(CONFIG_PTP_1588_CLOCK) */ static inline int ice_ptp_set_ts_config (struct ice_pf *pf,struct ifreq *ifr) {
@@ -384,6 +385,10 @@ static inline void ice_ptp_link_change(s { } +static inline void ice_ptp_queue_work (struct ice_pf *pf) +{ +} +static inline int ice_ptp_clock_index (struct ice_pf *pf) {return -1 ;
Greg Kroah-Hartman
maintainerPATCH 219/244 idpf: check error for register_netdev() on init
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Emil Tantilov <[email protected]>
[ Upstream commit 680811c67906191b237bbafe7dabbbad64649b39 ]
Current init logic ignores the error code from register_netdev(), which will cause WARN_ON() on attempt to unregister it, if there was one, and there is no info for the user that the creation of the netdev failed.
WARNING: CPU: 89 PID: 6902 at net/core/dev.c:11512 unregister_netdevice_many_notify+0x211/0x1a10 ... [ 3707.563641] unregister_netdev+0x1c/0x30 [ 3707.563656] idpf_vport_dealloc+0x5cf/0xce0 [idpf] [ 3707.563684] idpf_deinit_task+0xef/0x160 [idpf] [ 3707.563712] idpf_vc_core_deinit+0x84/0x320 [idpf] [ 3707.563739] idpf_remove+0xbf/0x780 [idpf] [ 3707.563769] pci_device_remove+0xab/0x1e0 [ 3707.563786] device_release_driver_internal+0x371/0x530 [ 3707.563803] driver_detach+0xbf/0x180 [ 3707.563816] bus_remove_driver+0x11b/0x2a0 [ 3707.563829] pci_unregister_driver+0x2a/0x250
Introduce an error check and log the vport number and error code. On removal make sure to check VPORT_REG_NETDEV flag prior to calling unregister and free on the netdev.
Add local variables for idx, vport_config and netdev for readability.
--- drivers/net/ethernet/intel/idpf/idpf_lib.c | 31 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_lib.c +22 -9
@@ -911,15 +911,19 @@ static int idpf_stop(struct net_device *static void idpf_decfg_netdev (struct idpf_vport *vport) {struct idpf_adapter *adapter = vport->adapter; +u16 idx = vport->idx;kfree (vport->rx_ptype_lkup); vport->rx_ptype_lkup =NULL ; -unregister_netdev (vport->netdev); -free_netdev (vport->netdev); +if (test_and_clear_bit (IDPF_VPORT_REG_NETDEV , + adapter->vport_config[idx]->flags)) { +unregister_netdev (vport->netdev); +free_netdev (vport->netdev); + } vport->netdev =NULL ; - adapter->netdevs[vport->idx] =NULL ; + adapter->netdevs[idx] =NULL ; }/**
@@ -1541,13 +1545,22 @@ void idpf_init_task(struct work_struct * }for (index =0 ; index < adapter->max_vports; index++) { -if (adapter->netdevs[index] && - !test_bit (IDPF_VPORT_REG_NETDEV , - adapter->vport_config[index]->flags)) { -register_netdev (adapter->netdevs[index]); -set_bit (IDPF_VPORT_REG_NETDEV , - adapter->vport_config[index]->flags); +struct net_device *netdev = adapter->netdevs[index]; +struct idpf_vport_config *vport_config; + + vport_config = adapter->vport_config[index]; + +if (!netdev || +test_bit (IDPF_VPORT_REG_NETDEV , vport_config->flags)) +continue ; + + err =register_netdev (netdev); +if (err) { +dev_err (&pdev->dev,"failed to register netdev for vport %d: %pe\n" , + index,ERR_PTR (err)); +continue ; } +set_bit (IDPF_VPORT_REG_NETDEV , vport_config->flags); }/* As all the required vports are created, clear the reset flag
Greg Kroah-Hartman
maintainerPATCH 220/244 idpf: detach and close netdevs while handling a reset
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Emil Tantilov <[email protected]>
[ Upstream commit 2e281e1155fc476c571c0bd2ffbfe28ab829a5c3 ]
Protect the reset path from callbacks by setting the netdevs to detached state and close any netdevs in UP state until the reset handling has completed. During a reset, the driver will de-allocate resources for the vport, and there is no guarantee that those will recover, which is why the existing vport_ctrl_lock does not provide sufficient protection.
idpf_detach_and_close() is called right before reset handling. If the reset handling succeeds, the netdevs state is recovered via call to idpf_attach_and_open(). If the reset handling fails the netdevs remain down. The detach/down calls are protected with RTNL lock to avoid racing with callbacks. On the recovery side the attach can be done without holding the RTNL lock as there are no callbacks expected at that point, due to detach/close always being done first in that flow.
The previous logic restoring the netdevs state based on the IDPF_VPORT_UP_REQUESTED flag in the init task is not needed anymore, hence the removal of idpf_set_vport_state(). The IDPF_VPORT_UP_REQUESTED is still being used to restore the state of the netdevs following the reset, but has no use outside of the reset handling flow.
idpf_init_hard_reset() is converted to void, since it was used as such and there is no error handling being done based on its return value.
Before this change, invoking hard and soft resets simultaneously will cause the driver to lose the vport state:
ip -br a
<inf> UP
echo 1 > /sys/class/net/ens801f0/device/reset& \
ethtool -L ens801f0 combined 8
ip -br a
<inf> DOWN
ip link set <inf> up
ip -br a
<inf> DOWN
Also in case of a failure in the reset path, the netdev is left exposed to external callbacks, while vport resources are not initialized, leading to a crash on subsequent ifup/down:
[408471.398966] idpf 0000:83:00.0: HW reset detected [408471.411744] idpf 0000:83:00.0: Device HW Reset initiated [408472.277901] idpf 0000:83:00.0: The driver was unable to contact the device's firmware. Check that the FW is running. Driver state= 0x2 [408508.125551] BUG: kernel NULL pointer dereference, address: 0000000000000078 [408508.126112] #PF: supervisor read access in kernel mode [408508.126687] #PF: error_code(0x0000) - not-present page [408508.127256] PGD 2aae2f067 P4D 0 [408508.127824] Oops: Oops: 0000 [#1] SMP NOPTI ... [408508.130871] RIP: 0010:idpf_stop+0x39/0x70 [idpf] ... [408508.139193] Call Trace: [408508.139637] <TASK> [408508.140077] __dev_close_many+0xbb/0x260 [408508.140533] __dev_change_flags+0x1cf/0x280 [408508.140987] netif_change_flags+0x26/0x70 [408508.141434] dev_change_flags+0x3d/0xb0 [408508.141878] devinet_ioctl+0x460/0x890 [408508.142321] inet_ioctl+0x18e/0x1d0 [408508.142762] ? _copy_to_user+0x22/0x70 [408508.143207] sock_do_ioctl+0x3d/0xe0 [408508.143652] sock_ioctl+0x10e/0x330 [408508.144091] ? find_held_lock+0x2b/0x80 [408508.144537] __x64_sys_ioctl+0x96/0xe0 [408508.144979] do_syscall_64+0x79/0x3d0 [408508.145415] entry_SYSCALL_64_after_hwframe+0x76/0x7e [408508.145860] RIP: 0033:0x7f3e0bb4caff
[Harshit: Backport to 6.12.y, resolve conflicts at 5 places:
These are caused to missing non-backportable commits:
1. commit: bd74a86bc75d ("idpf: link NAPIs to queues")
2. commit: f4312e6bfa2a ("idpf: implement core RDMA auxiliary dev create, init, and destroy")
3. commit: 8dd72ebc73f3 ("idpf: convert vport state to bitmap")
4. commit: bf86a012e676 ("idpf: implement remaining IDC RDMA core callbacks and handlers") in 6.12.y]
--- drivers/net/ethernet/intel/idpf/idpf_lib.c | 115 +++++++++++++++++------------ 1 file changed, 70 insertions(+), 45 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_lib.c +70 -45
@@ -693,6 +693,65 @@ static int idpf_init_mac_addr(struct idpreturn 0 ; } +static void idpf_detach_and_close (struct idpf_adapter *adapter) +{ +int max_vports = adapter->max_vports; + +for (int i =0 ; i < max_vports; i++) { +struct net_device *netdev = adapter->netdevs[i]; + +/* If the interface is in detached state, that means the +* previous reset was not handled successfully for this +* vport. +*/ +if (!netif_device_present (netdev)) +continue ; + +/* Hold RTNL to protect racing with callbacks */ +rtnl_lock (); +netif_device_detach (netdev); +if (netif_running (netdev)) { +set_bit (IDPF_VPORT_UP_REQUESTED , + adapter->vport_config[i]->flags); +dev_close (netdev); + } +rtnl_unlock (); + } +} + +static void idpf_attach_and_open (struct idpf_adapter *adapter) +{ +int max_vports = adapter->max_vports; + +for (int i =0 ; i < max_vports; i++) { +struct idpf_vport *vport = adapter->vports[i]; +struct idpf_vport_config *vport_config; +struct net_device *netdev; + +/* In case of a critical error in the init task, the vport +* will be freed. Only continue to restore the netdevs +* if the vport is allocated. +*/ +if (!vport) +continue ; + +/* No need for RTNL on attach as this function is called +* following detach and dev_close(). We do take RTNL for +* dev_open() below as it can race with external callbacks +* following the call to netif_device_attach(). +*/ + netdev = adapter->netdevs[i]; +netif_device_attach (netdev); + vport_config = adapter->vport_config[vport->idx]; +if (test_and_clear_bit (IDPF_VPORT_UP_REQUESTED , + vport_config->flags)) { +rtnl_lock (); +dev_open (netdev,NULL ); +rtnl_unlock (); + } + } +} +/** * idpf_cfg_netdev - Allocate, configure and register a netdev * @vport: main vport structure
@@ -990,10 +1049,11 @@ static void idpf_vport_dealloc(struct idunsigned int i = vport->idx;idpf_deinit_mac_addr (vport); -idpf_vport_stop (vport); -if (!test_bit (IDPF_HR_RESET_IN_PROG , adapter->flags)) +if (!test_bit (IDPF_HR_RESET_IN_PROG , adapter->flags)) { +idpf_vport_stop (vport);idpf_decfg_netdev (vport); + }if (test_bit (IDPF_REMOVE_IN_PROG , adapter->flags))idpf_del_all_mac_filters (vport);
@@ -1471,7 +1531,6 @@ void idpf_init_task(struct work_struct *struct idpf_vport_config *vport_config;struct idpf_vport_max_q max_q;struct idpf_adapter *adapter; -struct idpf_netdev_priv *np;struct idpf_vport *vport;u16 num_default_vports;struct pci_dev *pdev;
@@ -1528,12 +1587,6 @@ void idpf_init_task(struct work_struct *if (idpf_cfg_netdev (vport)) goto unwind_vports; -/* Once state is put into DOWN, driver is ready for dev_open */ - np =netdev_priv (vport->netdev); - np->state = __IDPF_VPORT_DOWN; -if (test_and_clear_bit (IDPF_VPORT_UP_REQUESTED , vport_config->flags)) -idpf_vport_open (vport); -/* Spawn and return 'idpf_init_task' work queue until all the * default vports are created */
@@ -1710,27 +1763,6 @@ static int idpf_check_reset_complete(str }/** -* idpf_set_vport_state - Set the vport state to be after the reset -* @adapter: Driver specific private structure -*/ -static void idpf_set_vport_state (struct idpf_adapter *adapter) -{ -u16 i; - -for (i =0 ; i < adapter->max_vports; i++) { -struct idpf_netdev_priv *np; - -if (!adapter->netdevs[i]) -continue ; - - np =netdev_priv (adapter->netdevs[i]); -if (np->state == __IDPF_VPORT_UP) -set_bit (IDPF_VPORT_UP_REQUESTED , - adapter->vport_config[i]->flags); - } -} - -/** * idpf_init_hard_reset - Initiate a hardware reset * @adapter: Driver specific private structure *
@@ -1738,35 +1770,23 @@ static void idpf_set_vport_state(struct* reallocate. Also reinitialize the mailbox. Return 0 on success, * negative on failure. */ -static int idpf_init_hard_reset (struct idpf_adapter *adapter) +static void idpf_init_hard_reset (struct idpf_adapter *adapter) {struct idpf_reg_ops *reg_ops = &adapter->dev_ops.reg_ops;struct device *dev = &adapter->pdev->dev; -struct net_device *netdev;int err; -u16 i; +idpf_detach_and_close (adapter);mutex_lock (&adapter->vport_ctrl_lock);dev_info (dev,"Device HW Reset initiated\n" ); -/* Avoid TX hangs on reset */ -for (i =0 ; i < adapter->max_vports; i++) { - netdev = adapter->netdevs[i]; -if (!netdev) -continue ; - -netif_carrier_off (netdev); -netif_tx_disable (netdev); - } -/* Prepare for reset */ if (test_and_clear_bit (IDPF_HR_DRV_LOAD , adapter->flags)) { reg_ops->trigger_reset (adapter,IDPF_HR_DRV_LOAD ); }else if (test_and_clear_bit (IDPF_HR_FUNC_RESET , adapter->flags)) {bool is_reset =idpf_is_reset_detected (adapter); -idpf_set_vport_state (adapter);idpf_vc_core_deinit (adapter);if (!is_reset) reg_ops->trigger_reset (adapter,IDPF_HR_FUNC_RESET );
@@ -1813,7 +1833,12 @@ static int idpf_init_hard_reset(struct i unlock_mutex:mutex_unlock (&adapter->vport_ctrl_lock); -return err; +/* Attempt to restore netdevs and initialize RDMA CORE AUX device, +* provided vc_core_init succeeded. It is still possible that +* vports are not allocated at this point if the init task failed. +*/ +if (!err) +idpf_attach_and_open (adapter); }/**
Greg Kroah-Hartman
maintainerPATCH 221/244 idpf: Fix RSS LUT NULL pointer crash on early ethtool operations
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sreedevi Joshi <[email protected]>
[ Upstream commit 83f38f210b85676f40ba8586b5a8edae19b56995 ]
The RSS LUT is not initialized until the interface comes up, causing the following NULL pointer crash when ethtool operations like rxhash on/off are performed before the interface is brought up for the first time.
Move RSS LUT initialization from ndo_open to vport creation to ensure LUT is always available. This enables RSS configuration via ethtool before bringing the interface up. Simplify LUT management by maintaining all changes in the driver's soft copy and programming zeros to the indirection table when rxhash is disabled. Defer HW programming until the interface comes up if it is down during rxhash and LUT configuration changes.
Steps to reproduce:
** Load idpf driver; interfaces will be created
modprobe idpf
** Before bringing the interfaces up, turn rxhash off
ethtool -K eth2 rxhash off
[89408.371875] BUG: kernel NULL pointer dereference, address: 0000000000000000 [89408.371908] #PF: supervisor read access in kernel mode [89408.371924] #PF: error_code(0x0000) - not-present page [89408.371940] PGD 0 P4D 0 [89408.371953] Oops: Oops: 0000 [#1] SMP NOPTI <snip> [89408.372052] RIP: 0010:memcpy_orig+0x16/0x130 [89408.372310] Call Trace: [89408.372317] <TASK> [89408.372326] ? idpf_set_features+0xfc/0x180 [idpf] [89408.372363] __netdev_update_features+0x295/0xde0 [89408.372384] ethnl_set_features+0x15e/0x460 [89408.372406] genl_family_rcv_msg_doit+0x11f/0x180 [89408.372429] genl_rcv_msg+0x1ad/0x2b0 [89408.372446] ? __pfx_ethnl_set_features+0x10/0x10 [89408.372465] ? __pfx_genl_rcv_msg+0x10/0x10 [89408.372482] netlink_rcv_skb+0x58/0x100 [89408.372502] genl_rcv+0x2c/0x50 [89408.372516] netlink_unicast+0x289/0x3e0 [89408.372533] netlink_sendmsg+0x215/0x440 [89408.372551] __sys_sendto+0x234/0x240 [89408.372571] __x64_sys_sendto+0x28/0x30 [89408.372585] x64_sys_call+0x1909/0x1da0 [89408.372604] do_syscall_64+0x7a/0xfa0 [89408.373140] ? clear_bhb_loop+0x60/0xb0 [89408.373647] entry_SYSCALL_64_after_hwframe+0x76/0x7e [89408.378887] </TASK>
<snip>
[Harshit: While this is a clean cherry-pick I had to change a line of code where test_bit(IDPF_VPORT_UP,..) is used because 6.12.y branch doesn't have commit: 8dd72ebc73f3 ("idpf: convert vport state to bitmap")]
--- drivers/net/ethernet/intel/idpf/idpf.h | 2 drivers/net/ethernet/intel/idpf/idpf_lib.c | 94 ++++++++++-------------- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 36 +++------ drivers/net/ethernet/intel/idpf/idpf_txrx.h | 4 - drivers/net/ethernet/intel/idpf/idpf_virtchnl.c | 9 ++ 5 files changed, 66 insertions(+), 79 deletions(-)
drivers/net/ethernet/intel/idpf/idpf.h +0 -2
@@ -361,14 +361,12 @@ enum idpf_user_flags {* @rss_key: RSS hash key * @rss_lut_size: Size of RSS lookup table * @rss_lut: RSS lookup table -* @cached_lut: Used to restore previously init RSS lut */ struct idpf_rss_data {u16 rss_key_size;u8 *rss_key;u16 rss_lut_size;u32 *rss_lut; -u32 *cached_lut; };/**
drivers/net/ethernet/intel/idpf/idpf_lib.c +43 -51
166 lines not on this page · raw message
drivers/net/ethernet/intel/idpf/idpf_txrx.c +13 -23
@@ -4068,57 +4068,47 @@ static void idpf_fill_dflt_rss_lut(struc rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; -for (i =0 ; i < rss_data->rss_lut_size; i++) { +for (i =0 ; i < rss_data->rss_lut_size; i++) rss_data->rss_lut[i] = i % num_active_rxq; - rss_data->cached_lut[i] = rss_data->rss_lut[i]; - } }/** -* idpf_init_rss - Allocate and initialize RSS resources +* idpf_init_rss_lut - Allocate and initialize RSS LUT * @vport: virtual port * -* Return 0 on success, negative on failure +* Return: 0 on success, negative on failure */ -int idpf_init_rss (struct idpf_vport *vport) +int idpf_init_rss_lut (struct idpf_vport *vport) {struct idpf_adapter *adapter = vport->adapter;struct idpf_rss_data *rss_data; -u32 lut_size; rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; +if (!rss_data->rss_lut) { +u32 lut_size; - lut_size = rss_data->rss_lut_size *sizeof (u32); - rss_data->rss_lut =kzalloc (lut_size,GFP_KERNEL ); -if (!rss_data->rss_lut) -return -ENOMEM ; - - rss_data->cached_lut =kzalloc (lut_size,GFP_KERNEL ); -if (!rss_data->cached_lut) { -kfree (rss_data->rss_lut); - rss_data->rss_lut =NULL ; - -return -ENOMEM ; + lut_size = rss_data->rss_lut_size *sizeof (u32); + rss_data->rss_lut =kzalloc (lut_size,GFP_KERNEL ); +if (!rss_data->rss_lut) +return -ENOMEM ; }/* Fill the default RSS lut values */ idpf_fill_dflt_rss_lut (vport); -return idpf_config_rss (vport); +return 0 ; }/** -* idpf_deinit_rss - Release RSS resources +* idpf_deinit_rss_lut - Release RSS LUT * @vport: virtual port */ -void idpf_deinit_rss (struct idpf_vport *vport) +void idpf_deinit_rss_lut (struct idpf_vport *vport) {struct idpf_adapter *adapter = vport->adapter;struct idpf_rss_data *rss_data; rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; -kfree (rss_data->cached_lut); - rss_data->cached_lut =NULL ;kfree (rss_data->rss_lut); rss_data->rss_lut =NULL ; }
drivers/net/ethernet/intel/idpf/idpf_txrx.h +2 -2
@@ -1018,8 +1018,8 @@ void idpf_vport_intr_deinit(struct idpf_int idpf_vport_intr_init (struct idpf_vport *vport);void idpf_vport_intr_ena (struct idpf_vport *vport);int idpf_config_rss (struct idpf_vport *vport); -int idpf_init_rss (struct idpf_vport *vport); -void idpf_deinit_rss (struct idpf_vport *vport); +int idpf_init_rss_lut (struct idpf_vport *vport); +void idpf_deinit_rss_lut (struct idpf_vport *vport);int idpf_rx_bufs_init_all (struct idpf_vport *vport);void idpf_rx_add_frag (struct idpf_rx_buf *rx_buf,struct sk_buff *skb,unsigned int size);
drivers/net/ethernet/intel/idpf/idpf_virtchnl.c +8 -1
@@ -2341,6 +2341,10 @@ int idpf_send_get_stats_msg(struct idpf_* @vport: virtual port data structure * @get: flag to set or get rss look up table * +* When rxhash is disabled, RSS LUT will be configured with zeros. If rxhash +* is enabled, the LUT values stored in driver's soft copy will be used to setup +* the HW. +* * Returns 0 on success, negative on failure. */ int idpf_send_get_set_rss_lut_msg (struct idpf_vport *vport,bool get)
@@ -2351,10 +2355,12 @@ int idpf_send_get_set_rss_lut_msg(structstruct idpf_rss_data *rss_data;int buf_size, lut_buf_size;ssize_t reply_sz; +bool rxhash_ena;int i; rss_data = &vport->adapter->vport_config[vport->idx]->user_config.rss_data; + rxhash_ena =idpf_is_feature_ena (vport,NETIF_F_RXHASH ); buf_size =struct_size (rl, lut, rss_data->rss_lut_size); rl =kzalloc (buf_size,GFP_KERNEL );if (!rl)
@@ -2376,7 +2382,8 @@ int idpf_send_get_set_rss_lut_msg(struct }else { rl->lut_entries =cpu_to_le16 (rss_data->rss_lut_size);for (i =0 ; i < rss_data->rss_lut_size; i++) - rl->lut[i] =cpu_to_le32 (rss_data->rss_lut[i]); + rl->lut[i] = rxhash_ena ? +cpu_to_le32 (rss_data->rss_lut[i]) :0 ; xn_params.vc_op =VIRTCHNL2_OP_SET_RSS_LUT ; }
Greg Kroah-Hartman
maintainerPATCH 222/244 idpf: Fix RSS LUT NULL ptr issue after soft reset
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sreedevi Joshi <[email protected]>
[ Upstream commit ebecca5b093895da801b3eba1a55b4ec4027d196 ]
During soft reset, the RSS LUT is freed and not restored unless the interface is up. If an ethtool command that accesses the rss lut is attempted immediately after reset, it will result in NULL ptr dereference. Also, there is no need to reset the rss lut if the soft reset does not involve queue count change.
After soft reset, set the RSS LUT to default values based on the updated queue count only if the reset was a result of a queue count change and the LUT was not configured by the user. In all other cases, don't touch the LUT.
Steps to reproduce:
** Bring the interface down (if up)
ifconfig eth1 down
** update the queue count (eg., 27->20)
ethtool -L eth1 combined 20
** display the RSS LUT
ethtool -x eth1
[82375.558338] BUG: kernel NULL pointer dereference, address: 0000000000000000 [82375.558373] #PF: supervisor read access in kernel mode [82375.558391] #PF: error_code(0x0000) - not-present page [82375.558408] PGD 0 P4D 0 [82375.558421] Oops: Oops: 0000 [#1] SMP NOPTI <snip> [82375.558516] RIP: 0010:idpf_get_rxfh+0x108/0x150 [idpf] [82375.558786] Call Trace: [82375.558793] <TASK> [82375.558804] rss_prepare.isra.0+0x187/0x2a0 [82375.558827] rss_prepare_data+0x3a/0x50 [82375.558845] ethnl_default_doit+0x13d/0x3e0 [82375.558863] genl_family_rcv_msg_doit+0x11f/0x180 [82375.558886] genl_rcv_msg+0x1ad/0x2b0 [82375.558902] ? __pfx_ethnl_default_doit+0x10/0x10 [82375.558920] ? __pfx_genl_rcv_msg+0x10/0x10 [82375.558937] netlink_rcv_skb+0x58/0x100 [82375.558957] genl_rcv+0x2c/0x50 [82375.558971] netlink_unicast+0x289/0x3e0 [82375.558988] netlink_sendmsg+0x215/0x440 [82375.559005] __sys_sendto+0x234/0x240 [82375.559555] __x64_sys_sendto+0x28/0x30 [82375.560068] x64_sys_call+0x1909/0x1da0 [82375.560576] do_syscall_64+0x7a/0xfa0 [82375.561076] ? clear_bhb_loop+0x60/0xb0 [82375.561567] entry_SYSCALL_64_after_hwframe+0x76/0x7e
<snip>
[Harshit: backport to 6.12.y, conflicts due to missing commit:8dd72ebc73f3 - idpf: convert vport state to bitmap andbd74a86bc75d -
idpf: link NAPIs to queues which changes idpf_vport_open/stop() APIs also take rtnl argument)
--- drivers/net/ethernet/intel/idpf/idpf_lib.c | 20 ++++---------------- drivers/net/ethernet/intel/idpf/idpf_txrx.c | 2 +- drivers/net/ethernet/intel/idpf/idpf_txrx.h | 1 + 3 files changed, 6 insertions(+), 17 deletions(-)
drivers/net/ethernet/intel/idpf/idpf_lib.c +4 -16
@@ -1405,8 +1405,6 @@ static int idpf_vport_open(struct idpf_v {struct idpf_netdev_priv *np =netdev_priv (vport->netdev);struct idpf_adapter *adapter = vport->adapter; -struct idpf_vport_config *vport_config; -struct idpf_rss_data *rss_data;int err;if (np->state != __IDPF_VPORT_DOWN)
@@ -1488,19 +1486,6 @@ static int idpf_vport_open(struct idpf_vidpf_restore_features (vport); - vport_config = adapter->vport_config[vport->idx]; - rss_data = &vport_config->user_config.rss_data; - -if (!rss_data->rss_lut) { - err =idpf_init_rss_lut (vport); -if (err) { -dev_err (&adapter->pdev->dev, -"Failed to initialize RSS LUT for vport %u: %d\n" , - vport->vport_id, err); - goto disable_vport; - } - } - err =idpf_config_rss (vport);if (err) { dev_err(&adapter->pdev->dev, "Failed to configure RSS for vport %u: %d\n",
@@ -1953,7 +1938,6 @@ int idpf_initiate_soft_reset(struct idpfidpf_vport_stop (vport); } -idpf_deinit_rss_lut (vport);/* We're passing in vport here because we need its wait_queue * to send a message and it should be getting all the vport * config data out of the adapter but we need to be careful not
@@ -1979,6 +1963,10 @@ int idpf_initiate_soft_reset(struct idpfif (err) goto err_open; +if (reset_cause ==IDPF_SR_Q_CHANGE && + !netif_is_rxfh_configured (vport->netdev)) +idpf_fill_dflt_rss_lut (vport); +if (current_state == __IDPF_VPORT_UP) err =idpf_vport_open (vport);
drivers/net/ethernet/intel/idpf/idpf_txrx.c +1 -1
@@ -4059,7 +4059,7 @@ int idpf_config_rss(struct idpf_vport *v* idpf_fill_dflt_rss_lut - Fill the indirection table with the default values * @vport: virtual port structure */ -static void idpf_fill_dflt_rss_lut (struct idpf_vport *vport) +void idpf_fill_dflt_rss_lut (struct idpf_vport *vport) {struct idpf_adapter *adapter = vport->adapter;u16 num_active_rxq = vport->num_rxq;
drivers/net/ethernet/intel/idpf/idpf_txrx.h +1 -0
@@ -1017,6 +1017,7 @@ void idpf_vport_intr_update_itr_ena_irq(void idpf_vport_intr_deinit (struct idpf_vport *vport);int idpf_vport_intr_init (struct idpf_vport *vport);void idpf_vport_intr_ena (struct idpf_vport *vport); +void idpf_fill_dflt_rss_lut (struct idpf_vport *vport);int idpf_config_rss (struct idpf_vport *vport);int idpf_init_rss_lut (struct idpf_vport *vport);void idpf_deinit_rss_lut (struct idpf_vport *vport);
Greg Kroah-Hartman
maintainerPATCH 223/244 ASoC: ak4458: Convert to RUNTIME_PM_OPS() & co
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
commit 9f9c8e9064ea8ceb13540a283f08550c097bb673 upstream.
Use the newer RUNTIME_PM_OPS() and SYSTEM_SLEEP_PM_OPS() macros instead of SET_RUNTIME_PM_OPS() and SET_SYSTEM_SLEEP_PM_OPS() together with pm_ptr(), which allows us dropping ugly __maybe_unused attributes and CONFIG_PM ifdefs.
This optimizes slightly when CONFIG_PM is disabled, too.
--- sound/soc/codecs/ak4458.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
sound/soc/codecs/ak4458.c +5 -8
@@ -639,8 +639,7 @@ static void ak4458_reset(struct ak4458_p } } -#ifdef CONFIG_PM -static int __maybe_unused ak4458_runtime_suspend (struct device *dev) +static int ak4458_runtime_suspend (struct device *dev) {struct ak4458_priv *ak4458 =dev_get_drvdata (dev);
@@ -656,7 +655,7 @@ static int __maybe_unused ak4458_runtimereturn 0 ; } -static int __maybe_unused ak4458_runtime_resume (struct device *dev) +static int ak4458_runtime_resume (struct device *dev) {struct ak4458_priv *ak4458 =dev_get_drvdata (dev);int ret;
@@ -686,7 +685,6 @@ err:regulator_bulk_disable (ARRAY_SIZE (ak4458->supplies), ak4458->supplies);return ret; } -#endif /* CONFIG_PM */ static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { .controls = ak4458_snd_controls,
@@ -735,9 +733,8 @@ static const struct ak4458_drvdata ak449 };static const struct dev_pm_ops ak4458_pm = { - SET_RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) -SET_SYSTEM_SLEEP_PM_OPS (pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) +SYSTEM_SLEEP_PM_OPS (pm_runtime_force_suspend, pm_runtime_force_resume) };static int ak4458_i2c_probe (struct i2c_client *i2c)
@@ -809,7 +806,7 @@ MODULE_DEVICE_TABLE(of, ak4458_of_match)static struct i2c_driver ak4458_i2c_driver = { .driver = { .name ="ak4458" , - .pm = &ak4458_pm, + .pm =pm_ptr (&ak4458_pm), .of_match_table = ak4458_of_match, }, .probe = ak4458_i2c_probe,
Greg Kroah-Hartman
maintainerPATCH 224/244 dmaengine: idxd: Fix not releasing workqueue on .release()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vinicius Costa Gomes <[email protected]>
[ Upstream commit 3d33de353b1ff9023d5ec73b9becf80ea87af695 ]
The workqueue associated with an DSA/IAA device is not released when the object is freed.
--- drivers/dma/idxd/sysfs.c | 1 + 1 file changed, 1 insertion(+)
drivers/dma/idxd/sysfs.c +1 -0
@@ -1810,6 +1810,7 @@ static void idxd_conf_device_release(struct device *dev) {struct idxd_device *idxd =confdev_to_idxd (dev); +destroy_workqueue (idxd->wq);kfree (idxd->groups);bitmap_free (idxd->wq_enable_map);kfree (idxd->wqs);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 225/244 dmaengine: idxd: Fix memory leak when a wq is reset
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vinicius Costa Gomes <[email protected]>
[ Upstream commit d9cfb5193a047a92a4d3c0e91ea4cc87c8f7c478 ]
idxd_wq_disable_cleanup() which is called from the reset path for a workqueue, sets the wq type to NONE, which for other parts of the driver mean that the wq is empty (all its resources were released).
Only set the wq type to NONE after its resources are released.
--- drivers/dma/idxd/device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/dma/idxd/device.c +1 -2
@@ -174,6 +174,7 @@ void idxd_wq_free_resources(struct idxd_wq *wq)free_descs (wq);dma_free_coherent (dev, wq->compls_size, wq->compls, wq->compls_addr);sbitmap_queue_free (&wq->sbq); + wq->type =IDXD_WQT_NONE ; }EXPORT_SYMBOL_NS_GPL (idxd_wq_free_resources,IDXD );
@@ -367,7 +368,6 @@ static void idxd_wq_disable_cleanup(struct idxd_wq *wq)lockdep_assert_held (&wq->wq_lock); wq->state =IDXD_WQ_DISABLED ;memset (wq->wqcfg,0 , idxd->wqcfg_size); - wq->type =IDXD_WQT_NONE ; wq->threshold =0 ; wq->priority =0 ; wq->enqcmds_retries =IDXD_ENQCMDS_RETRIES ;
@@ -1513,7 +1513,6 @@ void idxd_drv_disable_wq(struct idxd_wq *wq)idxd_wq_reset (wq);idxd_wq_free_resources (wq);percpu_ref_exit (&wq->wq_active); - wq->type =IDXD_WQT_NONE ; wq->client_count =0 ; }EXPORT_SYMBOL_NS_GPL (idxd_drv_disable_wq,IDXD );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 226/244 dmaengine: idxd: Fix freeing the allocated ida too late
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vinicius Costa Gomes <[email protected]>
[ Upstream commit c311f5e9248471a950f0a524c2fd736414d98900 ]
It can happen that when the cdev .release() is called, the driver already called ida_destroy(). Move ida_free() to the _del() path.
We see with DEBUG_KOBJECT_RELEASE enabled and forcing an early PCI unbind.
--- drivers/dma/idxd/cdev.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/dma/idxd/cdev.c +4 -4
@@ -161,11 +161,7 @@ static const struct device_type idxd_cdev_file_type = {static void idxd_cdev_dev_release (struct device *dev) {struct idxd_cdev *idxd_cdev =dev_to_cdev (dev); -struct idxd_cdev_context *cdev_ctx; -struct idxd_wq *wq = idxd_cdev->wq; - cdev_ctx = &ictx[wq->idxd->data->type]; -ida_free (&cdev_ctx->minor_ida, idxd_cdev->minor);kfree (idxd_cdev); }
@@ -585,11 +581,15 @@ int idxd_wq_add_cdev(struct idxd_wq *wq)void idxd_wq_del_cdev (struct idxd_wq *wq) { +struct idxd_cdev_context *cdev_ctx;struct idxd_cdev *idxd_cdev; idxd_cdev = wq->idxd_cdev; wq->idxd_cdev =NULL ;cdev_device_del (&idxd_cdev->cdev,cdev_dev (idxd_cdev)); + + cdev_ctx = &ictx[wq->idxd->data->type]; +ida_free (&cdev_ctx->minor_ida, idxd_cdev->minor);put_device (cdev_dev (idxd_cdev)); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 227/244 phy: ti: j721e-wiz: Fix device node reference leak in wiz_get_lane_phy_types()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Gu <[email protected]>
[ Upstream commit 584b457f4166293bdfa50f930228e9fb91a38392 ]
The serdes device_node is obtained using of_get_child_by_name(), which increments the reference count. However, it is never put, leading to a reference leak.
Add the missing of_node_put() calls to ensure the reference count is properly balanced.
--- drivers/phy/ti/phy-j721e-wiz.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/phy/ti/phy-j721e-wiz.c +2 -0
@@ -1426,6 +1426,7 @@ static int wiz_get_lane_phy_types(struct device *dev, struct wiz *wiz)dev_err (dev,"%s: Reading \"reg\" from \"%s\" failed: %d\n" , __func__, subnode->name, ret); +of_node_put (serdes);return ret; }of_property_read_u32 (subnode,"cdns,num-lanes" , &num_lanes);
@@ -1440,6 +1441,7 @@ static int wiz_get_lane_phy_types(struct device *dev, struct wiz *wiz) } } +of_node_put (serdes);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 228/244 dmaengine: dw-edma: Fix multiple times setting of the CYCLE_STATE and CYCLE_BIT bits for HDMA.
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: LUO Haowen <[email protected]>
[ Upstream commit 3f63297ff61a994b99d710dcb6dbde41c4003233 ]
Others have submitted this issue (https://lore.kernel.org/dmaengine/
[email protected]/),
but it has not been fixed yet. Therefore, more supplementary information is provided here.
As mentioned in the "PCS-CCS-CB-TCB" Producer-Consumer Synchronization of "DesignWare Cores PCI Express Controller Databook, version 6.00a":
1. The Consumer CYCLE_STATE (CCS) bit in the register only needs to be initialized once; the value will update automatically to be ~CYCLE_BIT (CB) in the next chunk.
2. The Consumer CYCLE_BIT bit in the register is loaded from the LL element and tested against CCS. When CB = CCS, the data transfer is executed. Otherwise not.
The current logic sets customer (HDMA) CS and CB bits to 1 in each chunk while setting the producer (software) CB of odd chunks to 0 and even chunks to 1 in the linked list. This is leading to a mismatch between the producer CB and consumer CS bits.
This issue can be reproduced by setting the transmission data size to exceed one chunk. By the way, in the EDMA using the same "PCS-CCS-CB-TCB" mechanism, the CS bit is only initialized once and this issue was not found. Refer to drivers/dma/dw-edma/dw-edma-v0-core.c:dw_edma_v0_core_start.
So fix this issue by initializing the CYCLE_STATE and CYCLE_BIT bits only once.
--- drivers/dma/dw-edma/dw-hdma-v0-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/dma/dw-edma/dw-hdma-v0-core.c +3 -3
@@ -252,10 +252,10 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) lower_32_bits(chunk->ll_region.paddr));SET_CH_32 (dw, chan->dir, chan->id, llp.msb,upper_32_bits (chunk->ll_region.paddr)); +/* Set consumer cycle */ +SET_CH_32 (dw, chan->dir, chan->id, cycle_sync, +HDMA_V0_CONSUMER_CYCLE_STAT |HDMA_V0_CONSUMER_CYCLE_BIT ); } -/* Set consumer cycle */ -SET_CH_32 (dw, chan->dir, chan->id, cycle_sync, -HDMA_V0_CONSUMER_CYCLE_STAT |HDMA_V0_CONSUMER_CYCLE_BIT );dw_hdma_v0_sync_ll_data (chunk);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 229/244 dmaengine: xilinx: xdma: Fix regmap init error handling
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alexander Stein <[email protected]>
[ Upstream commit e0adbf74e2a0455a6bc9628726ba87bcd0b42bf8 ]
devm_regmap_init_mmio returns an ERR_PTR() upon error, not NULL. Fix the error check and also fix the error message. Use the error code from ERR_PTR() instead of the wrong value in ret.
--- drivers/dma/xilinx/xdma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/dma/xilinx/xdma.c +2 -2
@@ -1240,8 +1240,8 @@ static int xdma_probe(struct platform_device *pdev) xdev->rmap =devm_regmap_init_mmio (&pdev->dev, reg_base, &xdma_regmap_config); -if (!xdev->rmap) { -xdma_err (xdev,"config regmap failed: %d" , ret); +if (IS_ERR (xdev->rmap)) { +xdma_err (xdev,"config regmap failed: %pe" , xdev->rmap); goto failed; }INIT_LIST_HEAD (&xdev->dma_dev.channels);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 230/244 netfs: Fix kernel BUG in netfs_limit_iter() for ITER_KVEC iterators
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Deepanshu Kartikey <[email protected]>
[ Upstream commit 67e467a11f62ff64ad219dc6aa5459e132c79d14 ]
When a process crashes and the kernel writes a core dump to a 9P filesystem, __kernel_write() creates an ITER_KVEC iterator. This iterator reaches netfs_limit_iter() via netfs_unbuffered_write(), which only handles ITER_FOLIOQ, ITER_BVEC and ITER_XARRAY iterator types, hitting the BUG() for any other type.
Fix this by adding netfs_limit_kvec() following the same pattern as netfs_limit_bvec(), since both kvec and bvec are simple segment arrays with pointer and length fields. Dispatch it from netfs_limit_iter() when the iterator type is ITER_KVEC.
--- fs/netfs/iterator.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
fs/netfs/iterator.c +43 -0
@@ -142,6 +142,47 @@ static size_t netfs_limit_bvec(const struct iov_iter *iter, size_t start_offset,return min (span, max_size); } +/* +* Select the span of a kvec iterator we're going to use. Limit it by both +* maximum size and maximum number of segments. Returns the size of the span +* in bytes. +*/ +static size_t netfs_limit_kvec (const struct iov_iter *iter,size_t start_offset, +size_t max_size,size_t max_segs) +{ +const struct kvec *kvecs = iter->kvec; +unsigned int nkv = iter->nr_segs, ix =0 , nsegs =0 ; +size_t len, span =0 , n = iter->count; +size_t skip = iter->iov_offset + start_offset; + +if (WARN_ON (!iov_iter_is_kvec (iter)) || +WARN_ON (start_offset > n) || + n ==0 ) +return 0 ; + +while (n && ix < nkv && skip) { + len = kvecs[ix].iov_len; +if (skip < len) +break ; + skip -= len; + n -= len; + ix++; + } + +while (n && ix < nkv) { + len =min3 (n, kvecs[ix].iov_len - skip, max_size); + span += len; + nsegs++; + ix++; +if (span >= max_size || nsegs >= max_segs) +break ; + skip =0 ; + n -= len; + } + +return min (span, max_size); +} +/* * Select the span of an xarray iterator we're going to use. Limit it by both * maximum size and maximum number of segments. It is assumed that segments
@@ -245,6 +286,8 @@ size_t netfs_limit_iter(const struct iov_iter *iter, size_t start_offset,return netfs_limit_bvec (iter, start_offset, max_size, max_segs);if (iov_iter_is_xarray (iter))return netfs_limit_xarray (iter, start_offset, max_size, max_segs); +if (iov_iter_is_kvec (iter)) +return netfs_limit_kvec (iter, start_offset, max_size, max_segs);BUG (); }EXPORT_SYMBOL (netfs_limit_iter);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 231/244 dmaengine: idxd: fix possible wrong descriptor completion in llist_abort_desc()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tuo Li <[email protected]>
[ Upstream commit e1c9866173c5f8521f2d0768547a01508cb9ff27 ]
At the end of this function, d is the traversal cursor of flist, but the code completes found instead. This can lead to issues such as NULL pointer dereferences, double completion, or descriptor leaks.
Fix this by completing d instead of found in the final list_for_each_entry_safe() loop.
--- drivers/dma/idxd/submit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dma/idxd/submit.c +1 -1
@@ -138,7 +138,7 @@ static void llist_abort_desc(struct idxd_wq *wq, struct idxd_irq_entry *ie,*/ list_for_each_entry_safe (d, t, &flist, list) {list_del_init (&d->list); -idxd_dma_complete_txd (found,IDXD_COMPLETE_ABORT , true, +idxd_dma_complete_txd (d,IDXD_COMPLETE_ABORT , true,NULL ,NULL ); } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 232/244 dmaengine: xilinx: xilinx_dma: Fix dma_device directions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Vasut <[email protected]>
[ Upstream commit e9cc95397bb7da13fe8a5b53a2f23cfaf9018ade ]
Unlike chan->direction , struct dma_device .directions field is a bitfield. Turn chan->direction into a bitfield to make it compatible with struct dma_device .directions .
--- drivers/dma/xilinx/xilinx_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/dma/xilinx/xilinx_dma.c +1 -1
@@ -2907,7 +2907,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,return -EINVAL ; } - xdev->common.directions |= chan->direction; + xdev->common.directions |=BIT (chan->direction);/* Request the interrupt */ chan->irq =of_irq_get (node, chan->tdest);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 233/244 dmaengine: xilinx: xilinx_dma: Fix residue calculation for cyclic DMA
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Vasut <[email protected]>
[ Upstream commit f61d145999d61948a23cd436ebbfa4c3b9ab8987 ]
The cyclic DMA calculation is currently entirely broken and reports residue only for the first segment. The problem is twofold.
First, when the first descriptor finishes, it is moved from active_list to done_list, but it is never returned back into the active_list. The xilinx_dma_tx_status() expects the descriptor to be in the active_list to report any meaningful residue information, which never happens after the first descriptor finishes. Fix this up in xilinx_dma_start_transfer() and if the descriptor is cyclic, lift it from done_list and place it back into active_list list.
Second, the segment .status fields of the descriptor remain dirty. Once the DMA did one pass on the descriptor, the .status fields are populated with data by the DMA, but the .status fields are not cleared before reuse during the next cyclic DMA round. The xilinx_dma_get_residue() recognizes that as if the descriptor was complete and had 0 residue, which is bogus. Reinitialize the status field before placing the descriptor back into the active_list.
--- drivers/dma/xilinx/xilinx_dma.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)
drivers/dma/xilinx/xilinx_dma.c +22 -1
@@ -1543,8 +1543,29 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan)if (chan->err)return ; -if (list_empty (&chan->pending_list)) +if (list_empty (&chan->pending_list)) { +if (chan->cyclic) { +struct xilinx_dma_tx_descriptor *desc; +struct list_head *entry; + + desc =list_last_entry (&chan->done_list, + struct xilinx_dma_tx_descriptor, node); +list_for_each (entry, &desc->segments) { +struct xilinx_axidma_tx_segment *axidma_seg; +struct xilinx_axidma_desc_hw *axidma_hw; + axidma_seg =list_entry (entry, + struct xilinx_axidma_tx_segment, + node); + axidma_hw = &axidma_seg->hw; + axidma_hw->status =0 ; + } + +list_splice_tail_init (&chan->done_list, &chan->active_list); + chan->desc_pendingcount =0 ; + chan->idle = false; + }return ; + }if (!chan->idle)return ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 234/244 dmaengine: xilinx: xilinx_dma: Fix unmasked residue subtraction
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marek Vasut <[email protected]>
[ Upstream commit c7d812e33f3e8ca0fa9eeabf71d1c7bc3acedc09 ]
The segment .control and .status fields both contain top bits which are not part of the buffer size, the buffer size is located only in the bottom max_buffer_len bits. To avoid interference from those top bits, mask out the size using max_buffer_len first, and only then subtract the values.
--- drivers/dma/xilinx/xilinx_dma.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/dma/xilinx/xilinx_dma.c +6 -6
@@ -996,16 +996,16 @@ static u32 xilinx_dma_get_residue(struct xilinx_dma_chan *chan, struct xilinx_cdma_tx_segment, node); cdma_hw = &cdma_seg->hw; - residue += (cdma_hw->control - cdma_hw->status) & - chan->xdev->max_buffer_len; + residue += (cdma_hw->control & chan->xdev->max_buffer_len) - + (cdma_hw->status & chan->xdev->max_buffer_len); }else if (chan->xdev->dma_config->dmatype ==XDMA_TYPE_AXIDMA ) { axidma_seg =list_entry (entry, struct xilinx_axidma_tx_segment, node); axidma_hw = &axidma_seg->hw; - residue += (axidma_hw->control - axidma_hw->status) & - chan->xdev->max_buffer_len; + residue += (axidma_hw->control & chan->xdev->max_buffer_len) - + (axidma_hw->status & chan->xdev->max_buffer_len); }else { aximcdma_seg = list_entry(entry,
@@ -1013,8 +1013,8 @@ static u32 xilinx_dma_get_residue(struct xilinx_dma_chan *chan, node); aximcdma_hw = &aximcdma_seg->hw; residue += - (aximcdma_hw->control - aximcdma_hw->status) & - chan->xdev->max_buffer_len; + (aximcdma_hw->control & chan->xdev->max_buffer_len) - + (aximcdma_hw->status & chan->xdev->max_buffer_len); } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 235/244 dmaengine: xilinx_dma: Fix reset related timeout with two-channel AXIDMA
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tomi Valkeinen <[email protected]>
[ Upstream commit a17ce4bc6f4f9acf77ba416c36791a15602e53aa ]
A single AXIDMA controller can have one or two channels. When it has two channels, the reset for both are tied together: resetting one channel resets the other as well. This creates a problem where resetting one channel will reset the registers for both channels, including clearing interrupt enable bits for the other channel, which can then lead to timeouts as the driver is waiting for an interrupt which never comes.
The driver currently has a probe-time work around for this: when a channel is created, the driver also resets and enables the interrupts. With two channels the reset for the second channel will clear the interrupt enables for the first one. The work around in the driver is just to manually enable the interrupts again in xilinx_dma_alloc_chan_resources().
This workaround only addresses the probe-time issue. When channels are reset at runtime (e.g., in xilinx_dma_terminate_all() or during error recovery), there's no corresponding mechanism to restore the other channel's interrupt enables. This leads to one channel having its interrupts disabled while the driver expects them to work, causing timeouts and DMA failures.
A proper fix is a complicated matter, as we should not reset the other channel when it's operating normally. So, perhaps, there should be some kind of synchronization for a common reset, which is not trivial to implement. To add to the complexity, the driver also supports other DMA types, like VDMA, CDMA and MCDMA, which don't have a shared reset.
However, when the two-channel AXIDMA is used in the (assumably) normal use case, providing DMA for a single memory-to-memory device, the common reset is a bit smaller issue: when something bad happens on one channel, or when one channel is terminated, the assumption is that we also want to terminate the other channel. And thus resetting both at the same time is "ok".
With that line of thinking we can implement a bit better work around than just the current probe time work around: let's enable the AXIDMA interrupts at xilinx_dma_start_transfer() instead. This ensures interrupts are enabled whenever a transfer starts, regardless of any prior resets that may have cleared them.
This approach is also more logical: enable interrupts only when needed for a transfer, rather than at resource allocation time, and, I think, all the other DMA types should also use this model, but I'm reluctant to do such changes as I cannot test them.
The reset function still enables interrupts even though it's not needed for AXIDMA anymore, but it's common code for all DMA types (VDMA, CDMA, MCDMA), so leave it unchanged to avoid affecting other variants.
--- drivers/dma/xilinx/xilinx_dma.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-)
drivers/dma/xilinx/xilinx_dma.c +1 -8
@@ -1216,14 +1216,6 @@ static int xilinx_dma_alloc_chan_resources(struct dma_chan *dchan)dma_cookie_init (dchan); -if (chan->xdev->dma_config->dmatype ==XDMA_TYPE_AXIDMA ) { -/* For AXI DMA resetting once channel will reset the -* other channel as well so enable the interrupts here. -*/ -dma_ctrl_set (chan,XILINX_DMA_REG_DMACR , -XILINX_DMA_DMAXR_ALL_IRQ_MASK ); - } -if ((chan->xdev->dma_config->dmatype ==XDMA_TYPE_CDMA ) && chan->has_sg)dma_ctrl_set (chan,XILINX_DMA_REG_DMACR ,XILINX_CDMA_CR_SGMODE );
@@ -1591,6 +1583,7 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) head_desc->async_tx.phys); reg &= ~XILINX_DMA_CR_DELAY_MAX ; reg |= chan->irq_delay <<XILINX_DMA_CR_DELAY_SHIFT ; + reg |=XILINX_DMA_DMAXR_ALL_IRQ_MASK ;dma_ctrl_write (chan,XILINX_DMA_REG_DMACR , reg);xilinx_dma_start (chan);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 236/244 btrfs: fix super block offset in error message in btrfs_validate_super()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mark Harmstone <[email protected]>
[ Upstream commit b52fe51f724385b3ed81e37e510a4a33107e8161 ]
Fix the superblock offset mismatch error message in btrfs_validate_super(): we changed it so that it considers all the superblocks, but the message still assumes we're only looking at the first one.
The change from %u to %llu is because we're changing from a constant to a u64.
--- fs/btrfs/disk-io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/btrfs/disk-io.c +2 -2
@@ -2468,8 +2468,8 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info,if (mirror_num >=0 &&btrfs_super_bytenr (sb) !=btrfs_sb_offset (mirror_num)) { -btrfs_err (fs_info,"super offset mismatch %llu != %u" , -btrfs_super_bytenr (sb),BTRFS_SUPER_INFO_OFFSET ); +btrfs_err (fs_info,"super offset mismatch %llu != %llu" , +btrfs_super_bytenr (sb),btrfs_sb_offset (mirror_num)); ret = -EINVAL ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 237/244 btrfs: fix leak of kobject name for sub-group space_info
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Shin'ichiro Kawasaki <[email protected]>
[ Upstream commit a4376d9a5d4c9610e69def3fc0b32c86a7ab7a41 ]
When create_space_info_sub_group() allocates elements of space_info->sub_group[], kobject_init_and_add() is called for each element via btrfs_sysfs_add_space_info_type(). However, when check_removing_space_info() frees these elements, it does not call btrfs_sysfs_remove_space_info() on them. As a result, kobject_put() is not called and the associated kobj->name objects are leaked.
This memory leak is reproduced by running the blktests test case zbd/009 on kernels built with CONFIG_DEBUG_KMEMLEAK. The kmemleak feature reports the following error:
unreferenced object 0xffff888112877d40 (size 16):
comm "mount", pid 1244, jiffies 4294996972
hex dump (first 16 bytes):
64 61 74 61 2d 72 65 6c 6f 63 00 c4 c6 a7 cb 7f data-reloc......
backtrace (crc 53ffde4d):
__kmalloc_node_track_caller_noprof+0x619/0x870
kstrdup+0x42/0xc0
kobject_set_name_vargs+0x44/0x110
kobject_init_and_add+0xcf/0x150
btrfs_sysfs_add_space_info_type+0xfc/0x210 [btrfs]
create_space_info_sub_group.constprop.0+0xfb/0x1b0 [btrfs]
create_space_info+0x211/0x320 [btrfs]
btrfs_init_space_info+0x15a/0x1b0 [btrfs]
open_ctree+0x33c7/0x4a50 [btrfs]
btrfs_get_tree.cold+0x9f/0x1ee [btrfs]
vfs_get_tree+0x87/0x2f0
vfs_cmd_create+0xbd/0x280
__do_sys_fsconfig+0x3df/0x990
do_syscall_64+0x136/0x1540
entry_SYSCALL_64_after_hwframe+0x76/0x7eTo avoid the leak, call btrfs_sysfs_remove_space_info() instead of kfree() for the elements.
--- fs/btrfs/block-group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/btrfs/block-group.c +1 -1
@@ -4452,7 +4452,7 @@ static void check_removing_space_info(struct btrfs_space_info *space_info)for (int i =0 ; i <BTRFS_SPACE_INFO_SUB_GROUP_MAX ; i++) {if (space_info->sub_group[i]) {check_removing_space_info (space_info->sub_group[i]); -kfree (space_info->sub_group[i]); +btrfs_sysfs_remove_space_info (space_info->sub_group[i]); space_info->sub_group[i] =NULL ; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 238/244 btrfs: fix lost error when running device stats on multiple devices fs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Filipe Manana <[email protected]>
[ Upstream commit 1c37d896b12dfd0d4c96e310b0033c6676933917 ]
Whenever we get an error updating the device stats item for a device in btrfs_run_dev_stats() we allow the loop to go to the next device, and if updating the stats item for the next device succeeds, we end up losing the error we had from the previous device.
Fix this by breaking out of the loop once we get an error and make sure it's returned to the caller. Since we are in the transaction commit path (and in the critical section actually), returning the error will result in a transaction abort.
--- fs/btrfs/volumes.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
fs/btrfs/volumes.c +3 -2
@@ -7738,8 +7738,9 @@ int btrfs_run_dev_stats(struct btrfs_trans_handle *trans)smp_rmb (); ret =update_dev_stat_item (trans, device); -if (!ret) -atomic_sub (stats_cnt, &device->dev_stats_ccnt); +if (ret) +break ; +atomic_sub (stats_cnt, &device->dev_stats_ccnt); }mutex_unlock (&fs_devices->device_list_mutex);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 239/244 xen/privcmd: unregister xenstore notifier on module exit
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: GuoHan Zhao <[email protected]>
[ Upstream commit cd7e1fef5a1ca1c4fcd232211962ac2395601636 ]
Commit 453b8fb68f36 ("xen/privcmd: restrict usage in unprivileged domU") added a xenstore notifier to defer setting the restriction target until Xenstore is ready.
XEN_PRIVCMD can be built as a module, but privcmd_exit() leaves that notifier behind. Balance the notifier lifecycle by unregistering it on module exit.
This is harmless even if xenstore was already ready at registration time and the notifier was never queued on the chain.
Message-ID: <[email protected]>
--- drivers/xen/privcmd.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/xen/privcmd.c +3 -0
@@ -1784,6 +1784,9 @@ static int __init privcmd_init(void) static void __exit privcmd_exit(void) { +if (!xen_initial_domain ()) +unregister_xenstore_notifier (&xenstore_notifier); +privcmd_ioeventfd_exit ();privcmd_irqfd_exit ();misc_deregister (&privcmd_dev);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 240/244 futex: Require sys_futex_requeue() to have identical flags
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Zijlstra <[email protected]>
[ Upstream commit 19f94b39058681dec64a10ebeb6f23fe7fc3f77a ]
Nicholas reported that his LLM found it was possible to create a UaF when sys_futex_requeue() is used with different flags. The initial motivation for allowing different flags was the variable sized futex, but since that hasn't been merged (yet), simply mandate the flags are identical, as is the case for the old style sys_futex() requeue operations.
--- kernel/futex/syscalls.c | 8 ++++++++ 1 file changed, 8 insertions(+)
kernel/futex/syscalls.c +8 -0
@@ -459,6 +459,14 @@ SYSCALL_DEFINE4(futex_requeue,if (ret)return ret; +/* +* For now mandate both flags are identical, like the sys_futex() +* interface has. If/when we merge the variable sized futex support, +* that patch can modify this test to allow a difference in size. +*/ +if (futexes[0 ].w.flags != futexes[1 ].w.flags) +return -EINVAL ; + cmpval = futexes[0 ].w.val; return futex_requeue(u64_to_user_ptr(futexes[0].w.uaddr), futexes[0].w.flags,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 241/244 dmaengine: idxd: Fix leaking event log memory
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vinicius Costa Gomes <[email protected]>
[ Upstream commit ee66bc29578391c9b48523dc9119af67bd5c7c0f ]
During the device remove process, the device is reset, causing the configuration registers to go back to their default state, which is zero. As the driver is checking if the event log support was enabled before deallocating, it will fail if a reset happened before.
Do not check if the support was enabled, the check for 'idxd->evl' being valid (only allocated if the HW capability is available) is enough.
--- drivers/dma/idxd/device.c | 4 ---- 1 file changed, 4 deletions(-)
drivers/dma/idxd/device.c +0 -4
@@ -815,10 +815,6 @@ static void idxd_device_evl_free(structstruct device *dev = &idxd->pdev->dev;struct idxd_evl *evl = idxd->evl; - gencfg.bits =ioread32 (idxd->reg_base +IDXD_GENCFG_OFFSET ); -if (!gencfg.evl_en) -return ; -mutex_lock (&evl->lock); gencfg.evl_en =0 ;iowrite32 (gencfg.bits, idxd->reg_base +IDXD_GENCFG_OFFSET );
Greg Kroah-Hartman
maintainerPATCH 242/244 net: bcmasp: Restore programming of TX map vector register
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fainelli <[email protected]>
commit 18ff09c1b94fa1584b31d3f4e9eecdca29230ce5 upstream.
On ASP versions v2.x we need to program the TX map vector register to properly exercise end-to-end flow control, otherwise the TX engine can either lock-up, or cause the hardware calculated checksum to be wrong/corrupted when multiple back to back packets are being submitted for transmission. This register defaults to 0, which means no flow control being applied.
--- drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c +3 -0
@@ -815,6 +815,9 @@ static void bcmasp_init_tx(struct bcmasp/* Tx SPB */ tx_spb_ctrl_wl (intf, ((intf->channel +8 ) <<TX_SPB_CTRL_XF_BID_SHIFT ),TX_SPB_CTRL_XF_CTRL2 ); + +if (intf->parent->tx_chan_offset) +tx_pause_ctrl_wl (intf, (1 << (intf->channel +8 )),TX_PAUSE_MAP_VECTOR );tx_spb_top_wl (intf,0x1e ,TX_SPB_TOP_BLKOUT );tx_spb_dma_wq (intf, intf->tx_spb_dma_addr,TX_SPB_DMA_READ );
Greg Kroah-Hartman
maintainerPATCH 243/244 net: bcmasp: Fix network filter wake for asp-3.0
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Justin Chen <[email protected]>
commit bbb11b8d758d17a4ce34b8ed0b49de150568265b upstream.
We need to apply the tx_chan_offset to the netfilter cfg channel or the output channel will be incorrect for asp-3.0 and newer.
--- drivers/net/ethernet/broadcom/asp2/bcmasp.c | 5 +++-- drivers/net/ethernet/broadcom/asp2/bcmasp.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-)
drivers/net/ethernet/broadcom/asp2/bcmasp.c +3 -2
@@ -156,7 +156,7 @@ static void bcmasp_netfilt_hw_en_wake(stASP_RX_FILTER_NET_OFFSET_L4 (32 ), ASP_RX_FILTER_NET_OFFSET(nfilt->hw_index + 1)); - rx_filter_core_wl(priv, ASP_RX_FILTER_NET_CFG_CH(nfilt->port + 8) | + rx_filter_core_wl(priv, ASP_RX_FILTER_NET_CFG_CH(nfilt->ch) | ASP_RX_FILTER_NET_CFG_EN | ASP_RX_FILTER_NET_CFG_L2_EN | ASP_RX_FILTER_NET_CFG_L3_EN |
@@ -166,7 +166,7 @@ static void bcmasp_netfilt_hw_en_wake(stASP_RX_FILTER_NET_CFG_UMC (nfilt->port), ASP_RX_FILTER_NET_CFG(nfilt->hw_index)); - rx_filter_core_wl(priv, ASP_RX_FILTER_NET_CFG_CH(nfilt->port + 8) | + rx_filter_core_wl(priv, ASP_RX_FILTER_NET_CFG_CH(nfilt->ch) | ASP_RX_FILTER_NET_CFG_EN | ASP_RX_FILTER_NET_CFG_L2_EN | ASP_RX_FILTER_NET_CFG_L3_EN |
@@ -714,6 +714,7 @@ struct bcmasp_net_filter *bcmasp_netfilt nfilter = &priv->net_filters[open_index]; nfilter->claimed = true; nfilter->port = intf->port; + nfilter->ch = intf->channel + priv->tx_chan_offset; nfilter->hw_index = open_index; }
drivers/net/ethernet/broadcom/asp2/bcmasp.h +1 -0
@@ -348,6 +348,7 @@ struct bcmasp_net_filter {bool wake_filter;int port; +int ch;unsigned int hw_index; };
Greg Kroah-Hartman
maintainerPATCH 244/244 idpf: nullify pointers after they are freed
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Li Li <[email protected]>
commit bc3b31977314433e4685ae92853d1b6e91ad7bc2 upstream.
rss_data->rss_key needs to be nullified after it is freed. Checks like "if (!rss_data->rss_key)" in the code could fail if it is not nullified.
Tested: built and booted the kernel.
--- drivers/net/ethernet/intel/idpf/idpf_lib.c | 1 + 1 file changed, 1 insertion(+)
drivers/net/ethernet/intel/idpf/idpf_lib.c +1 -0
@@ -1230,6 +1230,7 @@ static struct idpf_vport *idpf_vport_all free_rss_key:kfree (rss_data->rss_key); + rss_data->rss_key =NULL ; free_vector_idxs:kfree (vport->q_vector_idxs); free_vport:
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Thu, 02 Apr 2026 16:16:56 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.12.80-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.thanks,
greg k-h
Tested on an amd64 laptop (Lenovo ThinkPad T14 Gen 1). Working well, no regressions observed.
Signature · 1 line
-Barry K. Nathan <[email protected]>
Am 31.03.2026 um 18:19 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Builds, boots and works on my 2-socket Ivy Bridge Xeon E5-2697 v2 server. No dmesg oddities or regressions found.
Beste Grüße,
Peter Schneider
Signature · 8 lines
Climb the mountain not to plant your flag, but to embrace the challenge, enjoy the air and behold the view. Climb it so you can see the world, not so the world can see you. -- David McCullough Jr. OpenPGP: 0xA3828BD796CCE11A8CADE8866E3A92C92C3FF244 Download: https://www.peters-netzplatz.de/download/pschneider1968_pub.asc https://keys.mailvelope.com/pks/lookup?op=get&[email protected] https://keys.mailvelope.com/pks/lookup?op=get&[email protected]
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 6.12.80-rc1-g77487557237f #1 SMP PREEMPT_DYNAMIC Wed Apr 1 07:12:56 -00 2026 x86_64 AMD Ryzen 9 9950X 16-Core Processor AuthenticAMD GNU/Linux
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Thu, 02 Apr 2026 16:16:56 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.12.80-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.thanks,
greg k-h
All tests passing for Tegra ...
Test results for stable-v6.12:
11 builds: 11 pass, 0 fail
28 boots: 28 pass, 0 fail
133 tests: 133 pass, 0 failLinux version: 6.12.80-rc1-g77487557237f
Boards tested: tegra124-jetson-tk1, tegra186-p2771-0000,
tegra186-p3509-0000+p3636-0001, tegra194-p2972-0000,
tegra194-p3509-0000+p3668-0000, tegra20-ventana,
tegra210-p2371-2180, tegra210-p3450-0000,
tegra234-p3737-0000+p3701-0000, tegra30-cardhu-a04Jon
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Thu, 02 Apr 2026 16:16:56 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.12.80-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Compiled and tested on
- Verdin AM62
- Verdin iMX8MP
- Toradex SMARC iMX8MP
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Thu, 02 Apr 2026 16:16:56 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 9 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.12.80-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.thanks,
greg k-h
Compiled and booted on my test system. No dmesg regressions.
thanks,
-- Shuah
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Hi Greg,
Quoted 4 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
No problems seen on x86_64 and aarch64 with our testing.
Thanks,
Harshit
Quoted 8 lines · On Tue, 31 Mar 2026 18:19:10 +0200 Greg Kroah-Hartman…
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Thu, 02 Apr 2026 16:16:56 +0000. Anything received after that time might be too late.
Boot-tested under QEMU for Rust x86_64, arm64 and riscv64; built-tested for loongarch64:
There are a bunch of CLIPPY=1 warnings (errors with CONFIG_WERROR) like this one on the pin-init change:
warning: unsafe block missing a safety comment
--> rust/kernel/init/macros.rs:1015:25
|
1015 | unsafe { ::core::pin::Pin::new_unchecked(slot) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
::: rust/kernel/block/mq/tag_set.rs:27:1
|
27 | #[pin_data(PinnedDrop)]
| ----------------------- in this procedural macro expansion
|
= help: consider adding a safety comment on the preceding line
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
= note: requested on the command line with `-W clippy::undocumented-unsafe-blocks`
= note: this warning originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info)It is not a huge deal, since they are just CLIPPY=1 warnings, but we nevertheless try to keep them clean.
Apart from that, the rest looks OK.
Thanks!
Cheers,
Miguel
Greg KH
maintainerQuoted 34 lines · Miguel Ojeda
Quoted 8 lines · On Tue, 31 Mar 2026 18:19:10 +0200 Greg Kroah-Hartman…
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Thu, 02 Apr 2026 16:16:56 +0000. Anything received after that time might be too late.
Boot-tested under QEMU for Rust
x86_64, arm64 and riscv64; built-tested for loongarch64:Tested-by Miguel Ojeda <[email protected]>There are a bunch of
CLIPPY=1warnings (errors withCONFIG_WERROR) like this one on the pin-init change:warning: unsafe block missing a safety comment --> rust/kernel/init/macros.rs:1015:25 | 1015 | unsafe { ::core::pin::Pin::new_unchecked(slot) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ::: rust/kernel/block/mq/tag_set.rs:27:1 | 27 | #[pin_data(PinnedDrop)] | ----------------------- in this procedural macro expansion | = help: consider adding a safety comment on the preceding line = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks = note: requested on the command line with `-W clippy::undocumented-unsafe-blocks` = note: this warning originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info)
Quoted 7 lines
It is not a huge deal, since they are just
CLIPPY=1warnings, but we nevertheless try to keep them clean.Apart from that, the rest looks OK.
Cc Benno Lossin <[email protected]>Cc Gary Guo <[email protected]>
Ok, I can live with clippy warnings, but will gladly take a fix-up patch to resolve them :)
thanks,
greg k-h
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
Responses should be made by Thu, 02 Apr 2026 16:16:56 +0000. Anything received after that time might be too late.
The whole patch series can be found in one patch at:
Quoted 8 lines
https://www.kernel.org/pub/linux/kernel/v6.x/stable-review/patch-6.12.80-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-6.12.y
and the diffstat can be found below.thanks,
greg k-h
On ARCH_BRCMSTB using 32-bit and 64-bit ARM kernels, build tested on BMIPS_GENERIC:
Signature · 1 line
Florian
Hi!
Quoted 4 lines
This is the start of the stable review cycle for the 6.12.80 release. There are 244 patches in this series, all will be posted as a response to this one. If anyone has any issues with these being applied, please let me know.
CIP testing did not find any problems here:
https://gitlab.com/cip-project/cip-testing/linux-stable-rc-ci/-/tree/linux-6.12.y
Best regards,
Pavel