7.1.4-rc1 review
Subsystems (2)
Greg Kroah-Hartman
maintainerPATCH 406/518 regulator: scmi: fix of_node refcount leak in scmi_regulator_probe()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Wentao Liang <[email protected]>
commit fa11039d6cdff84584a3ef8cc1f5e1b56e045da2 upstream.
scmi_regulator_probe() calls of_find_node_by_name() which takes a reference on the returned device node. On the error path where process_scmi_regulator_of_node() fails, the function returns without calling of_node_put() on the child node, leaking the reference.
Add of_node_put(np) on the error path to properly release the reference.
--- drivers/regulator/scmi-regulator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/regulator/scmi-regulator.c +3 -1
@@ -345,8 +345,10 @@ static int scmi_regulator_probe(struct sfor_each_child_of_node_scoped (np, child) { ret =process_scmi_regulator_of_node (sdev, ph, child, rinfo);/* abort on any mem issue */ -if (ret == -ENOMEM ) +if (ret == -ENOMEM ) { +of_node_put (np);return ret; + } }of_node_put (np);/*
Greg Kroah-Hartman
maintainerPATCH 407/518 i2c: core: fix hang on adapter registration failure
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 3c7e164344e5bcf6f274bbf59a3274f5caad9bc1 upstream.
Clients may be registered from bus notifier callbacks when the adapter is registered. On a subsequent error during registration, the adapter references taken by such clients prevent the wait for the references to be released from ever completing.
Fix this by refactoring client deregistration and deregistering also on late adapter registration failures.
--- drivers/i2c/i2c-core-base.c | 49 ++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 20 deletions(-)
drivers/i2c/i2c-core-base.c +29 -20
@@ -63,6 +63,7 @@static DEFINE_MUTEX (core_lock);static DEFINE_IDR (i2c_adapter_idr); +static void i2c_deregister_clients (struct i2c_adapter *adap);static int i2c_detect (struct i2c_adapter *adapter,struct i2c_driver *driver);static DEFINE_STATIC_KEY_FALSE (i2c_trace_msg_key);
@@ -1608,6 +1609,7 @@ static int i2c_register_adapter(struct ireturn 0 ; out_reg: +i2c_deregister_clients (adap);debugfs_remove_recursive (adap->debugfs);init_completion (&adap->dev_released);device_unregister (&adap->dev);
@@ -1747,29 +1749,10 @@ static int __process_removed_adapter(strreturn 0 ; } -/** -* i2c_del_adapter - unregister I2C adapter -* @adap: the adapter being unregistered -* Context: can sleep -* -* This unregisters an I2C adapter which was previously registered -* by @i2c_add_adapter or @i2c_add_numbered_adapter. -*/ -void i2c_del_adapter (struct i2c_adapter *adap) +static void i2c_deregister_clients (struct i2c_adapter *adap) { -struct i2c_adapter *found;struct i2c_client *client, *next; -/* First make sure that this adapter was ever added */ -mutex_lock (&core_lock); - found =idr_find (&i2c_adapter_idr, adap->nr); -mutex_unlock (&core_lock); -if (found != adap) { -pr_debug ("attempting to delete unregistered adapter [%s]\n" , adap->name); -return ; - } - -i2c_acpi_remove_space_handler (adap);/* Tell drivers about this removal */ mutex_lock (&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap,
@@ -1795,6 +1778,32 @@ void i2c_del_adapter(struct i2c_adapter* them up properly, so we give them a chance to do that first. */ device_for_each_child (&adap->dev,NULL , __unregister_client);device_for_each_child (&adap->dev,NULL , __unregister_dummy); +} + +/** +* i2c_del_adapter - unregister I2C adapter +* @adap: the adapter being unregistered +* Context: can sleep +* +* This unregisters an I2C adapter which was previously registered +* by @i2c_add_adapter or @i2c_add_numbered_adapter. +*/ +void i2c_del_adapter (struct i2c_adapter *adap) +{ +struct i2c_adapter *found; + +/* First make sure that this adapter was ever added */ +mutex_lock (&core_lock); + found =idr_find (&i2c_adapter_idr, adap->nr); +mutex_unlock (&core_lock); +if (found != adap) { +pr_debug ("attempting to delete unregistered adapter [%s]\n" , adap->name); +return ; + } + +i2c_acpi_remove_space_handler (adap); + +i2c_deregister_clients (adap);/* device name is gone after device_unregister */ dev_dbg (&adap->dev,"adapter [%s] unregistered\n" , adap->name);
Greg Kroah-Hartman
maintainerPATCH 408/518 perf/aux: Fix page UAF in map_range()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lee Jia Jie <[email protected]>
commit 5948aaf64f81f217a25dcc2bf6c0779bca19566c upstream.
map_range() reads rb->aux_pages[], rb->aux_nr_pages and rb->aux_pgoff via perf_mmap_to_page() while holding only event->mmap_mutex. Those fields are serialized by rb->aux_mutex, and mmap_mutex is per event.
Thus, two events sharing one rb via PERF_EVENT_IOC_SET_OUTPUT can race rb_alloc_aux() with map_range(), leading to a page-UAF scenario as follows:
CPU 0 CPU 1
===== =====
rb_alloc_aux() map_range()
[1]: allocate rb->aux_pages[0]
[2]: rb->aux_nr_pages++
[3]: perf_mmap_to_page()
returns rb->aux_pages[0]
[4]: map it as VM_PFNMAP [5]: rb->aux_pgoff = 1
munmap the page
[6]: free rb->aux_pages[0]
Pages mapped as VM_PFNMAP have no refcount protection, so CPU 1 holds a mapping to a freed physical frame.
Fix this by taking rb->aux_mutex across the page walk in map_range().
--- kernel/events/core.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/events/core.c +2 -0
@@ -7150,6 +7150,8 @@ static int map_range(struct perf_bufferint err =0 ;unsigned long pagenum; +guard (mutex)(&rb->aux_mutex); +/* * We map this as a VM_PFNMAP VMA. *
Greg Kroah-Hartman
maintainerPATCH 409/518 liveupdate: reject LIVEUPDATE_IOCTL_CREATE_SESSION with invalid name length
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luca Boccassi <[email protected]>
commit e947433cd0d2b95a277757451b9b9c2714136dc2 upstream.
A session name must not be an empty string, and must not exceed the maximum size define in the uapi header, including null termination.
--- kernel/liveupdate/luo_session.c | 4 ++++ 1 file changed, 4 insertions(+)
kernel/liveupdate/luo_session.c +4 -0
@@ -382,9 +382,13 @@ static int luo_session_getfile(struct luint luo_session_create (const char *name,struct file **filep) { +size_t len =strnlen (name,LIVEUPDATE_SESSION_NAME_LENGTH );struct luo_session *session;int err; +if (len ==0 || len >LIVEUPDATE_SESSION_NAME_LENGTH -1 ) +return -EINVAL ; + session =luo_session_alloc (name);if (IS_ERR (session))return PTR_ERR (session);
Greg Kroah-Hartman
maintainerPATCH 410/518 selftests/liveupdate: add test cases for LIVEUPDATE_IOCTL_CREATE_SESSION calls with invalid length
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luca Boccassi <[email protected]>
commit dab2b4c66aa0f44ccb6a0096906e5680c604fe39 upstream.
Verify that LIVEUPDATE_IOCTL_CREATE_SESSION ioctl which provide a name that is an empty string or too long are not allowed.
--- tools/testing/selftests/liveupdate/liveupdate.c | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
tools/testing/selftests/liveupdate/liveupdate.c +42 -0
@@ -386,4 +386,46 @@ TEST_F(liveupdate_device, prevent_doubleASSERT_EQ (close (session_fd2),0 ); } +/* +* Test Case: Create Session with No Null Termination +* +* Verifies that filling the entire 64-byte name field with non-null characters +* (no '\0' terminator) is rejected by the kernel with EINVAL. +*/ +TEST_F(liveupdate_device, create_session_no_null_termination) +{ +struct liveupdate_ioctl_create_session args = {}; + + self->fd1 =open (LIVEUPDATE_DEV ,O_RDWR ); +if (self->fd1 <0 && errno ==ENOENT ) +SKIP (return,"%s does not exist" ,LIVEUPDATE_DEV ); +ASSERT_GE (self->fd1,0 ); + +/* Fill entire name field with 'X', no null terminator */ + args.size =sizeof (args); +memset (args.name,'X' ,sizeof (args.name)); + +EXPECT_LT (ioctl (self->fd1,LIVEUPDATE_IOCTL_CREATE_SESSION , &args),0 ); +EXPECT_EQ (errno,EINVAL ); +} + +/* +* Test Case: Create Session with Empty Name +* +* Verifies that creating a session with an empty string name fails +* with EINVAL. +*/ +TEST_F(liveupdate_device, create_session_empty_name) +{ +int session_fd; + + self->fd1 =open (LIVEUPDATE_DEV ,O_RDWR ); +if (self->fd1 <0 && errno ==ENOENT ) +SKIP (return,"%s does not exist" ,LIVEUPDATE_DEV ); +ASSERT_GE (self->fd1,0 ); + + session_fd =create_session (self->fd1,"" ); +EXPECT_EQ (session_fd, -EINVAL ); +} +TEST_HARNESS_MAIN
Greg Kroah-Hartman
maintainerPATCH 411/518 tracing: Prevent out-of-bounds read in glob matching
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huihui Huang <[email protected]>
commit 0a6070839b1ef276d5b05bedfb787743e140fb17 upstream.
String event fields are not necessarily NUL-terminated, so the filter predicate functions (filter_pred_string(), filter_pred_strloc() and filter_pred_strrelloc()) pass the field length to the regex match callbacks, and the length-aware matchers honour it.
regex_match_glob() was the exception: it ignored the length and called glob_match(), which scans the string until it hits a NUL byte. Some string fields are not NUL-terminated. One example is the dynamic char array of the xfs_* namespace tracepoints, which is copied without a trailing NUL. For such a field, glob matching reads past the end of the event field, causing a KASAN slab-out-of-bounds read in glob_match(), reached via regex_match_glob() and filter_match_preds() from the xfs_lookup tracepoint.
Add a length-bounded glob_match_len() and use it from regex_match_glob() so glob matching always stops at the field boundary. The matching loop is factored into a shared helper so glob_match() keeps its behaviour.
--- include/linux/glob.h | 1 + kernel/trace/trace_events_filter.c | 6 ++---- lib/glob.c | 31 +++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 6 deletions(-)
include/linux/glob.h +1 -0
@@ -6,5 +6,6 @@#include <linux/compiler.h> /* For __pure */ bool __pure glob_match (char const *pat,char const *str); +bool __pure glob_match_len (char const *pat,char const *str,size_t len);#endif /* _LINUX_GLOB_H */
kernel/trace/trace_events_filter.c +2 -4
@@ -1056,11 +1056,9 @@ static int regex_match_end(char *str, streturn 0 ; } -static int regex_match_glob (char *str,struct regex *r,int len__maybe_unused ) +static int regex_match_glob (char *str,struct regex *r,int len) { -if (glob_match (r->pattern, str)) -return 1 ; -return 0 ; +return glob_match_len (r->pattern, str, len) ?1 :0 ; }/**
lib/glob.c +29 -2
@@ -11,6 +11,9 @@MODULE_DESCRIPTION ("glob(7) matching" );MODULE_LICENSE ("Dual MIT/GPL" ); +static bool __pure glob_match_str (char const *pat,char const *str, +char const *str_end); +/** * glob_match - Shell-style pattern matching, like !fnmatch(pat, str, 0) * @pat: Shell-style pattern to match, e.g. "*.[ch]".
@@ -41,6 +44,29 @@ MODULE_LICENSE("Dual MIT/GPL");*/ bool __pure glob_match (char const *pat,char const *str) { +return glob_match_str (pat, str,NULL ); +} +EXPORT_SYMBOL (glob_match); + +/** +* glob_match_len - glob match against a length-bounded string +* @pat: Shell-style pattern to match. +* @str: String to match. Need not be NUL-terminated. +* @len: Number of bytes of @str that may be read. +* +* Like glob_match(), but @str is only read up to @len bytes, so it can be +* used on buffers that are not NUL-terminated (e.g. trace event fields). +* A NUL byte within @len still terminates the string. +*/ +bool __pure glob_match_len (char const *pat,char const *str,size_t len) +{ +return glob_match_str (pat, str, str + len); +} +EXPORT_SYMBOL (glob_match_len); + +static bool __pure glob_match_str (char const *pat,char const *str, +char const *str_end) +{/* * Backtrack to previous * on mismatch and retry starting one * character later in the string. Because * matches all characters
@@ -55,9 +81,11 @@ bool __pure glob_match(char const *pat,* on mismatch, or true after matching the trailing nul bytes. */ for (;;) { -unsigned char c = *str++; +unsigned char c = (str_end && str >= str_end) ?'\0' : *str;unsigned char d = *pat++; + str++; +switch (d) {case '?' :/* Wildcard: anything but nul */ if (c =='\0' )
@@ -125,4 +153,3 @@ backtrack: } } } -EXPORT_SYMBOL (glob_match);
Greg Kroah-Hartman
maintainerPATCH 412/518 audit: fix potential integer overflow in audit_log_n_hex()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ricardo Robaina <[email protected]>
commit 65dfde57d1e29ce2b76fc23dd565eccd5c0bc0f0 upstream.
The function calculates new_len as len << 1 for hex encoding. This has two overflow risks: the shift itself can overflow when len is large, and the result can be truncated when assigned to new_len (declared as int) from the size_t calculation.
Fix by using check_shl_overflow() to catch shift overflow and changing new_len and loop counter i to size_t to prevent truncation.
--- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
kernel/audit.c +9 -2
@@ -62,6 +62,7 @@#include <net/ip.h> #include <net/ipv6.h> #include <linux/sctp.h> +#include <linux/overflow.h> #include "audit.h"
@@ -2080,7 +2081,8 @@ void audit_log_format(struct audit_buffevoid audit_log_n_hex (struct audit_buffer *ab,const unsigned char *buf,size_t len) { -int i, avail, new_len; +int avail; +size_t i, new_len;unsigned char *ptr;struct sk_buff *skb;
@@ -2090,7 +2092,12 @@ void audit_log_n_hex(struct audit_bufferBUG_ON (!ab->skb); skb = ab->skb; avail =skb_tailroom (skb); - new_len = len<<1 ; + +if (check_shl_overflow (len,1 , &new_len)) { +audit_log_format (ab,"?" ); +return ; + } +if (new_len >= avail) {/* Round the buffer request up to the next multiple */ new_len =AUDIT_BUFSIZ *(((new_len-avail)/AUDIT_BUFSIZ ) +1 );
Greg Kroah-Hartman
maintainerPATCH 413/518 NFSv4: include MAY_WRITE in open permission mask for O_TRUNC
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Benjamin Coddington <[email protected]>
commit 5140f099ecd8a2f2808b7f7b720ee1bad8468974 upstream.
POSIX requires write permission to truncate a file, so an open() that specifies O_TRUNC must be authorized for write access regardless of the O_ACCMODE access mode.
nfs_open_permission_mask() builds the access mask passed to nfs_may_open(), which is the local authorization gate for OPENs the client serves itself from a cached write delegation via the can_open_delegated() path in nfs4_try_open_cached(). The mask is derived from O_ACCMODE alone, so an open(O_RDONLY | O_TRUNC) against a file the caller cannot write requests only MAY_READ and passes the local check. The OPEN is then satisfied locally and the truncation is issued to the server as a SETATTR(size=0) over the delegation stateid, which the server accepts under standard write-delegation semantics. POSIX requires that this open fail with EACCES.
Include MAY_WRITE in the mask whenever O_TRUNC is set so the local check matches the access the server would have enforced.
--- fs/nfs/dir.c | 2 ++ 1 file changed, 2 insertions(+)
fs/nfs/dir.c +2 -0
@@ -3344,6 +3344,8 @@ static int nfs_open_permission_mask(int mask |=MAY_READ ;if ((openflags &O_ACCMODE ) !=O_RDONLY ) mask |=MAY_WRITE ; +if (openflags &O_TRUNC ) + mask |=MAY_WRITE ; }return mask;
Greg Kroah-Hartman
maintainerPATCH 414/518 rqspinlock: Fix order in raw_res_spin_(un)lock_irq to allow schedule
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Gabriele Monaco <[email protected]>
commit b48bd16eb9fc57a463a337ca148516cdf3212d61 upstream.
raw_res_spin_unlock_irqrestore() calls raw_res_spin_unlock() and then restores interrupts, this means preemption is enabled when interrupts are still disabled (as part of raw_res_spin_unlock()) so this cannot trigger an actual preemption.
This is inconsistent with other spinlock implementations (raw_spin_unlock_irqrestore() and bpf_res_spin_unlock_irqrestore() itself).
Adjust the macro to ensure interrupts are enabled before enabling preemption, allowing to schedule at that point. Make the same modification in the error path of raw_res_spin_lock_irqsave().
--- include/asm-generic/rqspinlock.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
include/asm-generic/rqspinlock.h +11 -3
@@ -243,12 +243,20 @@ static __always_inline void res_spin_unl ({ \int __ret; \local_irq_save (flags); \ - __ret =raw_res_spin_lock (lock); \ -if (__ret) \ +preempt_disable (); \ + __ret =res_spin_lock (lock); \ +if (__ret) { \local_irq_restore (flags); \ +preempt_enable (); \ + } \ __ret; \ }) -#define raw_res_spin_unlock_irqrestore (lock, flags) ({ raw_res_spin_unlock(lock); local_irq_restore(flags); }) +#define raw_res_spin_unlock_irqrestore (lock, flags) \ + ({ \ + res_spin_unlock(lock); \ + local_irq_restore(flags); \ + preempt_enable(); \ + })#endif /* __ASM_GENERIC_RQSPINLOCK_H */
Greg Kroah-Hartman
maintainerPATCH 415/518 module: decompress: check return value of module_extend_max_pages()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andrii Kuchmenko <[email protected]>
commit 786d2d84416a9a1c1a47b71a68d679d886284be2 upstream.
module_extend_max_pages() calls kvrealloc() internally and returns -ENOMEM on allocation failure. The return value is never checked.
If the initial allocation fails, info->pages remains NULL and info->max_pages remains 0. Subsequent calls to module_get_next_page() will attempt to dynamically grow the array by calling module_extend_max_pages(info, 0) since info->used_pages is 0. This results in kvrealloc(NULL, 0) returning ZERO_SIZE_PTR, which is treated as a success, leading to a dereference of ZERO_SIZE_PTR and a kernel oops.
Fix: add the missing error check after module_extend_max_pages() and return immediately on failure. This matches the pattern used by every other kvrealloc() caller in the module loading path.
--- kernel/module/decompress.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/module/decompress.c +2 -0
@@ -307,6 +307,8 @@ int module_decompress(struct load_info **/ n_pages =DIV_ROUND_UP (size,PAGE_SIZE ) *2 ; error =module_extend_max_pages (info, n_pages); +if (error) +return error; data_size =MODULE_DECOMPRESS_FN (info, buf, size);if (data_size <0 ) {
Greg Kroah-Hartman
maintainerPATCH 416/518 vt: fix spurious modifier in CSI/cursor key sequences
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolas Pitre <[email protected]>
commit e9ad4d5ca309cb517d3f7a85251c3c5328f40f1f upstream.
csi_modifier_param() builds the xterm modifier parameter from shift_state, counting KG_SHIFTL/KG_SHIFTR as Shift, KG_ALTGR as Alt and KG_CTRLL/KG_CTRLR as Ctrl in addition to the canonical KG_SHIFT, KG_ALT and KG_CTRL.
That is wrong when those weights are not plain modifiers. Keymaps derived from XKB layouts (by kbd's xkbsupport, and by the console-setup used in Debian, Ubuntu and others) encode the active layout group using KG_SHIFTL/KG_SHIFTR:
group 1: -
group 2: shiftl
group 3: shiftr
group 4: shiftl | shiftr
So while a non-default layout group is selected, KG_SHIFTL and/or KG_SHIFTR are set in shift_state with no Shift key held. csi_modifier_param() then adds a spurious Shift to every cursor and CSI key: pressing Up while group 2 is active emits ESC[1;2A (Shift+Up) instead of ESC[A. KG_ALTGR has the same problem since it is the standard third-level selector.
Normal keymaps bind the physical Shift/Ctrl/Alt keys to KG_SHIFT, KG_CTRL and KG_ALT, leaving the left/right and AltGr weights free for layout and level selection. Count only those canonical weights, so genuine modifiers are still encoded while layout/level selectors are not.
--- drivers/tty/vt/keyboard.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
drivers/tty/vt/keyboard.c +9 -3
@@ -765,16 +765,22 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag)/* * Compute xterm-style modifier parameter for CSI sequences. * Returns 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0) +* +* Only the canonical modifier weights are counted. The left/right variants +* (KG_SHIFTL, KG_SHIFTR, KG_CTRLL, KG_CTRLR) and KG_ALTGR are commonly +* repurposed as keymap layout-group or level selectors rather than as plain +* modifiers (for instance XKB-derived keymaps select the layout group with +* KG_SHIFTL/KG_SHIFTR), so counting them would encode a spurious modifier. */ static int csi_modifier_param (void ) {int mod =1 ; -if (shift_state & (BIT (KG_SHIFT ) |BIT (KG_SHIFTL ) |BIT (KG_SHIFTR ))) +if (shift_state &BIT (KG_SHIFT )) mod +=1 ; -if (shift_state & (BIT (KG_ALT ) |BIT (KG_ALTGR ))) +if (shift_state &BIT (KG_ALT )) mod +=2 ; -if (shift_state & (BIT (KG_CTRL ) |BIT (KG_CTRLL ) |BIT (KG_CTRLR ))) +if (shift_state &BIT (KG_CTRL )) mod +=4 ;return mod; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 417/518 exfat: preserve benign secondary entries during rename and move
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rochan Avlur <[email protected]>
commit 942296784b2a9439651750c42f540bf2579b330f upstream.
Commit 8258ef28001a ("exfat: handle unreconized benign secondary entries") added cluster freeing for benign secondary entries inside exfat_remove_entries(). However, exfat_remove_entries() is also called from the rename and move paths (exfat_rename_file and exfat_move_file), where the old entry set is being relocated rather than deleted. This causes benign secondary entries such as vendor extension entries to be silently destroyed on rename or cross-directory move, violating the exFAT spec requirement (section 8.2) that implementations preserve unrecognized benign secondary entries.
Fix this by adding a free_benign parameter to exfat_remove_entries() so callers can suppress cluster freeing during relocation, and extending exfat_init_ext_entry() to copy trailing benign secondary entries from the old entry set into the new one internally. Also clean up the error paths to delete newly allocated entries on failure.
--- fs/exfat/dir.c | 50 +++++++++++++++++++++++++---- fs/exfat/exfat_fs.h | 5 +- fs/exfat/namei.c | 89 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 116 insertions(+), 28 deletions(-)
fs/exfat/dir.c +44 -6
@@ -470,32 +470,70 @@ static void exfat_free_benign_secondary_exfat_free_cluster (inode, &dir); } +/* +* exfat_init_ext_entry - initialize extension entries in a directory entry set +* @es: target entry set +* @num_entries: number of entries excluding benign secondary entries +* @p_uniname: filename to store +* @old_es: optional source entry set with benign secondary entries, or NULL +* @num_extra: number of benign secondary entries to copy from @old_es +* +* Set up the file, stream extension, and filename entries in @es, optionally +* preserving @num_extra benign secondary entries from @old_es. @es and @old_es +* may refer to the same entry set; excess entries are marked as deleted. +*/ void exfat_init_ext_entry (struct exfat_entry_set_cache *es,int num_entries, -struct exfat_uni_name *p_uniname) +struct exfat_uni_name *p_uniname, +struct exfat_entry_set_cache *old_es,int num_extra) { -int i; +int i, src_start =0 , old_num;unsigned short *uniname = p_uniname->name;struct exfat_dentry *ep; - es->num_entries = num_entries; +if (WARN_ON (num_extra <0 || (num_extra && (!old_es || + old_es->num_entries <ES_IDX_FIRST_FILENAME + num_extra)))) + num_extra =0 ; + +/* +* Save old entry count and source position before modifying +* es->num_entries, since old_es and es may point to the same +* entry set. +*/ + old_num = es->num_entries; +if (old_es && num_extra >0 ) + src_start = old_es->num_entries - num_extra; + + es->num_entries = num_entries + num_extra; ep =exfat_get_dentry_cached (es,ES_IDX_FILE ); - ep->dentry.file.num_ext = (unsigned char )(num_entries -1 ); + ep->dentry.file.num_ext = (unsigned char )(num_entries -1 + num_extra); ep =exfat_get_dentry_cached (es,ES_IDX_STREAM ); ep->dentry.stream.name_len = p_uniname->name_len; ep->dentry.stream.name_hash =cpu_to_le16 (p_uniname->name_hash); +if (old_es && num_extra >0 ) { +for (i =0 ; i < num_extra; i++) + *exfat_get_dentry_cached (es, num_entries + i) = + *exfat_get_dentry_cached (old_es, src_start + i); + } +for (i =ES_IDX_FIRST_FILENAME ; i < num_entries; i++) { ep =exfat_get_dentry_cached (es, i);exfat_init_name_entry (ep, uniname); uniname +=EXFAT_FILE_NAME_LEN ; } +/* Mark excess old entries as deleted (in-place shrink) */ +for (i = num_entries + num_extra; i < old_num; i++) { + ep =exfat_get_dentry_cached (es, i); +exfat_set_entry_type (ep,TYPE_DELETED ); + } +exfat_update_dir_chksum (es); }void exfat_remove_entries (struct inode *inode,struct exfat_entry_set_cache *es, -int order) +int order,bool free_benign) {int i;struct exfat_dentry *ep;
@@ -503,7 +541,7 @@ void exfat_remove_entries(struct inode *for (i = order; i < es->num_entries; i++) { ep =exfat_get_dentry_cached (es, i); -if (exfat_get_entry_type (ep) &TYPE_BENIGN_SEC ) +if (free_benign && (exfat_get_entry_type (ep) &TYPE_BENIGN_SEC ))exfat_free_benign_secondary_clusters (inode, ep);exfat_set_entry_type (ep,TYPE_DELETED );
fs/exfat/exfat_fs.h +3 -2
@@ -524,9 +524,10 @@ void exfat_init_dir_entry(struct exfat_e unsigned int type, unsigned int start_clu, unsigned long long size, struct timespec64 *ts);void exfat_init_ext_entry (struct exfat_entry_set_cache *es,int num_entries, -struct exfat_uni_name *p_uniname); +struct exfat_uni_name *p_uniname, +struct exfat_entry_set_cache *old_es,int num_extra);void exfat_remove_entries (struct inode *inode,struct exfat_entry_set_cache *es, -int order); +int order,bool free_benign);void exfat_update_dir_chksum (struct exfat_entry_set_cache *es);int exfat_calc_num_entries (struct exfat_uni_name *p_uniname); int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
fs/exfat/namei.c +69 -20
184 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 418/518 exfat: bound uniname advance in exfat_find_dir_entry()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 3a1230e7b043c62737b05a3e9275ca83a43ad20a upstream.
In exfat_find_dir_entry(), each TYPE_EXTEND (file name) entry advances the output pointer by a fixed amount while the loop guard only tracks the accumulated name length:
if (++order == 2) uniname = p_uniname->name; else uniname += EXFAT_FILE_NAME_LEN; len = exfat_extract_uni_name(ep, entry_uniname); name_len += len; unichar = *(uniname+len); *(uniname+len) = 0x0;
uniname grows by EXFAT_FILE_NAME_LEN (15) per name entry, but name_len grows only by the actual extracted length, which is shorter when a name fragment contains an early NUL. The only guard isname_len >= MAX_NAME_LENGTH, so a crafted directory with many short name fragments lets uniname run far past the p_uniname->name[MAX_NAME_LENGTH + 3] buffer while name_len stays small, causing an out-of-bounds read and write at *(uniname+len).
The sibling extractor exfat_get_uniname_from_ext_entry() already stops on a short fragment (the lockstep len != EXFAT_FILE_NAME_LEN guard added in commit d42334578eba ("exfat: check if filename entries exceeds max filename length")); exfat_find_dir_entry() never got the equivalent. Track the per-entry write offset as a count and reject a fragment once the offset, or the offset plus the extracted length, would exceed MAX_NAME_LENGTH, before forming the output pointer.
--- fs/exfat/dir.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
fs/exfat/dir.c +8 -5
@@ -1067,6 +1067,7 @@ rewind:if (entry_type ==TYPE_EXTEND ) {unsigned short entry_uniname[16 ], unichar; +unsigned int offset;if (step !=DIRENT_STEP_NAME || name_len >=MAX_NAME_LENGTH ) {
@@ -1075,13 +1076,15 @@ rewind:continue ; } -if (++order ==2 ) - uniname = p_uniname->name; -else - uniname +=EXFAT_FILE_NAME_LEN ; - + offset = (++order -2 ) *EXFAT_FILE_NAME_LEN ; len =exfat_extract_uni_name (ep, entry_uniname);brelse (bh); +if (offset >MAX_NAME_LENGTH || + len >MAX_NAME_LENGTH - offset) { + step =DIRENT_STEP_FILE ; +continue ; + } + uniname = p_uniname->name + offset; name_len += len; unichar = *(uniname+len);
Greg Kroah-Hartman
maintainerPATCH 419/518 NTB: epf: Fix request_irq() unwind in ntb_epf_init_isr()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Koichiro Den <[email protected]>
commit fcba26efe5efc7441f5505f4ccc69791214b40be upstream.
ntb_epf_init_isr() requests multiple MSI/MSI-X vectors in a loop. If request_irq() fails part-way through, it jumps straight to pci_free_irq_vectors() without freeing already requested IRQs.
Fix the error path by freeing any successfully requested IRQs before releasing the vectors.
--- drivers/ntb/hw/epf/ntb_hw_epf.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
drivers/ntb/hw/epf/ntb_hw_epf.c +4 -6
@@ -357,7 +357,7 @@ static int ntb_epf_init_isr(struct ntb_e 0, "ntb_epf", ndev);if (ret) {dev_err (dev,"Failed to request irq\n" ); - goto err_request_irq; + goto err_free_irq; } }
@@ -367,16 +367,14 @@ static int ntb_epf_init_isr(struct ntb_e argument | irq);if (ret) {dev_err (dev,"Failed to configure doorbell\n" ); - goto err_configure_db; + goto err_free_irq; }return 0 ; -err_configure_db: -for (i =0 ; i < ndev->db_count +1 ; i++) +err_free_irq: +while (i--)free_irq (pci_irq_vector (pdev, i), ndev); - -err_request_irq:pci_free_irq_vectors (pdev);return ret;
Greg Kroah-Hartman
maintainerPATCH 420/518 riscv: mm: Define DIRECT_MAP_PHYSMEM_END
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vivian Wang <[email protected]>
commit f3336b48cf9d3f2d1fc78e3289c0ded2f00876ee upstream.
On RISC-V, the actual mappable range of physical address space is dependent on the current MMU mode i.e. satp_mode (See Documentation/arch/riscv/vm-layout.rst).
Define the DIRECT_MAP_PHYSMEM_END macro based on the existing virtual address space layout macros to expose this information to get_free_mem_region(). Otherwise, it returns a region that couldn't be mapped, which breaks ZONE_DEVICE.
--- arch/riscv/include/asm/pgtable.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
arch/riscv/include/asm/pgtable.h +10 -0
@@ -93,6 +93,16 @@*/ #define vmemmap ((struct page *)VMEMMAP_START - vmemmap_start_pfn) +/* Needed to limit get_free_mem_region() */ +#if defined(CONFIG_FLATMEM ) +#define DIRECT_MAP_PHYSMEM_END (phys_ram_base + KERN_VIRT_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM_VMEMMAP ) +#define DIRECT_MAP_PHYSMEM_END \ + ((vmemmap_start_pfn + VMEMMAP_SIZE / sizeof(struct page)) * PAGE_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM ) +/* DIRECT_MAP_PHYSMEM_END is not limited by VA space assignment in this case */ +#endif +#define PCI_IO_SIZE SZ_16M#define PCI_IO_END VMEMMAP_START#define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE)
Greg Kroah-Hartman
maintainerPATCH 421/518 riscv: mm: Unconditionally sfence.vma for spurious fault
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vivian Wang <[email protected]>
commit 1b2c6b56a9fa0dcbef461039937de22b1cbecc7d upstream.
Svvptc does not guarantee that it's safe to just return here. Since we have already cleared our bit, if, theoretically, the bounded timeframe for the accessed page to become valid still hasn't happened after sret, we could fault again and actually crash.
Hopefully, these spurious faults should be rare enough that this is an acceptable slowdown.
--- arch/riscv/kernel/entry.S | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
arch/riscv/kernel/entry.S +5 -2
@@ -75,8 +75,11 @@ /* Atomically reset the current cpu bit in new_vmalloc */ amoxor.d a0, a1, (a0) - /* Only emit a sfence.vma if the uarch caches invalid entries */ - ALTERNATIVE("sfence.vma", "nop", 0, RISCV_ISA_EXT_SVVPTC, 1) + /* + * A sfence.vma is required here. Even if we had Svvptc, there's no + * guarantee that after returning we wouldn't just fault again. + */ + sfence.vma REG_L a0, TASK_TI_A0(tp) REG_L a1, TASK_TI_A1(tp)
Greg Kroah-Hartman
maintainerPATCH 422/518 lib/test_hmm: use kvfree() to free kvcalloc() allocations
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hao Ge <[email protected]>
commit 59f19bf6f119eecfa16355186b593abba8eb5198 upstream.
Coccinelle scripts/coccinelle/api/kfree_mismatch.cocci reports the following warnings:
lib/test_hmm.c:1256:15-16: WARNING kvmalloc is used to allocate this memory at line 1191 lib/test_hmm.c:1257:15-16: WARNING kvmalloc is used to allocate this memory at line 1196
Fix this by replacing kfree() with kvfree() to correctly handle the vmalloc() fallback path of kvcalloc().
--- lib/test_hmm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
lib/test_hmm.c +2 -2
@@ -1253,8 +1253,8 @@ static int dmirror_migrate_to_device(struct dmirror *dmirror,mmap_read_unlock (mm);mmput (mm); free_mem: -kfree (src_pfns); -kfree (dst_pfns); +kvfree (src_pfns); +kvfree (dst_pfns);return ret; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 423/518 mm: fix mmap errno value when MAP_DROPPABLE is not supported
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Anthony Yznaga <[email protected]>
commit d86c9e971af2315119a78c564a802fafcebf1b6b upstream.
Patch series "fix MAP_DROPPABLE not supported errno", v4.
Mark Brown reported seeing a regression in -next on 32 bit arm with the mlock selftests. Before exiting and marking the tests failed, the following message was logged after an attempt to create a MAP_DROPPABLE mapping:
Bail out! mmap error: Unknown error 524
It turns out error 524 is ENOTSUPP which is an error that userspace is not supposed to see, but it indicates in this instance that MAP_DROPPABLE is not supported.
The first patch changes the errno returned to EOPNOTSUPP. The second patch is a second version of a prior patch to introduce selftests to verify locking behavior with droppable mappings with the additional change to skip the tests when MAP_DROPPABLE is not supported. The third patch fixes the MAP_DROPPABLE selftest so that it is run by the framework and skips if MAP_DROPPABLE is not supported.
This patch (of 3):
On configs where MAP_DROPPABLE is not supported (currently any 32-bit config except for PPC32), mmap fails with errno set to ENOTSUPP. However, ENOTSUPP is not a standard error value that userspace knows about. The acceptable userspace-visible errno to use is EOPNOTSUPP. checkpatch.pl has a warning to this effect.
--- mm/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
mm/mmap.c +1 -1
@@ -504,7 +504,7 @@ unsigned long do_mmap(struct file *file,break ;case MAP_DROPPABLE :if (VM_DROPPABLE ==VM_NONE ) -return -ENOTSUPP ; +return -EOPNOTSUPP ;/* * A locked or stack area makes no sense to be droppable. *
Greg Kroah-Hartman
maintainerPATCH 424/518 selftests: mm: fix and speedup "droppable" test
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Hildenbrand (Arm) <[email protected]>
commit cc13a7a618fe8354f16d74c06aaf9565a68e9ebd upstream.
The droppable test currently relies on creating memory pressure in a child process to trigger dropping the droppable pages.
That not only takes a long time on some machines (allocating and filling all that memory), on large machines this will not work as we hardcode the area size to 134217728 bytes.
... further, we rely on timeouts to detect that memory was not dropped, which is really suboptimal.
Instead, let's just use MADV_PAGEOUT on a 2 MiB region. MADV_PAGEOUT works with droppable memory even without swap.
There is the low chance of MADV_PAGEOUT failing to drop a page because of speculative references. We'll wait 1s and retry 10 times to rule that unlikely case out as best as we can.
On a machine without swap:
$ ./droppable TAP version 13 1..1 ok 1 madvise(MADV_PAGEOUT) behavior # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
--- tools/testing/selftests/mm/droppable.c | 46 ++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 20 deletions(-)
tools/testing/selftests/mm/droppable.c +26 -20
@@ -17,10 +17,10 @@int main (int argc,char *argv[]) { -size_t alloc_size =134217728 ; -size_t page_size =getpagesize (); +const size_t alloc_size =2 *1024 *1024 ; +int retry_count =10 ; +bool dropped;void *alloc; -pid_t child;ksft_print_header ();ksft_set_plan (1 );
@@ -28,26 +28,32 @@ int main(int argc, char *argv[]) alloc =mmap (0 , alloc_size,PROT_READ |PROT_WRITE ,MAP_ANONYMOUS |MAP_DROPPABLE ,-1 ,0 );assert (alloc !=MAP_FAILED );memset (alloc,'A' , alloc_size); -for (size_t i =0 ; i < alloc_size; i += page_size) -assert (*(uint8_t *)(alloc + i)); - child =fork (); -assert (child >=0 ); -if (!child) { -for (;;) - *(char *)malloc (page_size) ='B' ; - } - -for (bool done = false; !done;) { -for (size_t i =0 ; i < alloc_size; i += page_size) { -if (!*(uint8_t *)(alloc + i)) { - done = true; -break ; +while (retry_count--) { +if (madvise (alloc, alloc_size,MADV_PAGEOUT )) { +if (errno ==EINVAL ) { +ksft_test_result_skip ("madvise(MADV_PAGEOUT) not supported\n" ); +exit (KSFT_SKIP ); } +ksft_test_result_fail ("madvise(MADV_PAGEOUT) error: %s\n" ,strerror (errno)); +exit (KSFT_FAIL ); } + + dropped =memchr (alloc,'A' , alloc_size) ==NULL ; + +/* +* Speculative reference can temporarily prevent some +* pages from getting dropped. So sleep and retry. +* +* If a page is not droppable for 10s, something +* is seriously messed up and we want to fail. +*/ +if (dropped) +break ; +sleep (1 ); } -kill (child,SIGTERM ); -ksft_test_result_pass ("MAP_DROPPABLE: PASS\n" ); -exit (KSFT_PASS ); +ksft_test_result (dropped,"madvise(MADV_PAGEOUT) behavior\n" ); + +ksft_finished (); }
Greg Kroah-Hartman
maintainerPATCH 425/518 mm: page_ext: add count limit to page_ext_iter_next to prevent invalid PFN access
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ketan <[email protected]>
commit ffd017237cfe99e6e5602ab14179b0e6878a0840 upstream.
The page_ext iteration API does not validate if the PFN still belongs to a valid section while advancing the iterator. When dynamically adding memory in the hotplug path, it can lead to a NULL pointer dereference during page_ext_lookup at the boundary of the last valid section when iterator count equals __pgcount.
The for_each_page_ext() macro calls page_ext_iter_next() as its loop increment. for_each_page_ext() does a "__page_ext = page_ext_iter_next(&__iter)" at the end. This causes page_ext_iter_next() to increment iter->index past __pgcount and call page_ext_lookup(start_pfn
+ __pgcount). During memory hotplug (online), the PFN at start_pfn + __pgcount may belong to a section that has not yet been initialized, causing page_ext_lookup() to trigger a NULL pointer dereference.
[ 14.555124][ T846] Call trace: [ 14.555125][ T846] lookup_page_ext+0x6c/0x108 (P) [ 14.555127][ T846] page_ext_lookup+0x30/0x3c [ 14.555129][ T846] __reset_page_owner+0x11c/0x260 [ 14.571201][ T846] __free_pages_ok+0x5e8/0x8e0 [ 14.571204][ T846] __free_pages_core+0x78/0xf0 [ 14.571206][ T846] generic_online_page+0x14/0x24 [ 14.597782][ T846] online_pages+0x178/0x30c [ 14.597784][ T846] memory_block_change_state+0x284/0x32c [ 14.597787][ T846] memory_subsys_online+0x4c/0x64 [ 14.597789][ T846] device_online+0x88/0xb0 [ 14.597791][ T846] online_memory_block+0x30/0x40 [ 14.597793][ T846] walk_memory_blocks+0xac/0xe8 [ 14.597794][ T846] add_memory_resource+0x280/0x298 [ 14.656161][ T846] add_memory+0x60/0x98
Move the iteration boundary enforcement inside the iterator functions, so callers cannot inadvertently access beyond the requested range.
--- include/linux/page_ext.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
include/linux/page_ext.h +13 -6
@@ -120,14 +120,18 @@ struct page_ext_iter {* page_ext_iter_begin() - Prepare for iterating through page extensions. * @iter: page extension iterator. * @pfn: PFN of the page we're interested in. +* @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no page_ext exists for this page. */ static inline struct page_ext *page_ext_iter_begin (struct page_ext_iter *iter, -unsigned long pfn) +unsigned long pfn,unsigned long count) { +if (!count) +return NULL ; + iter->index =0 ; iter->start_pfn = pfn; iter->page_ext =page_ext_lookup (pfn);
@@ -138,19 +142,22 @@ static inline struct page_ext *page_ext_/** * page_ext_iter_next() - Get next page extension * @iter: page extension iterator. +* @count: maximum number of page extensions to return. * * Must be called with RCU read lock taken. * * Return: NULL if no next page_ext exists. */ -static inline struct page_ext *page_ext_iter_next (struct page_ext_iter *iter) +static inline struct page_ext *page_ext_iter_next (struct page_ext_iter *iter, +unsigned long count) {unsigned long pfn;if (WARN_ON_ONCE (!iter->page_ext))return NULL ; - iter->index++; +if (++iter->index >= count) +return NULL ; pfn = iter->start_pfn + iter->index;if (page_ext_iter_next_fast_possible (pfn))
@@ -183,9 +190,9 @@ static inline struct page_ext *page_ext_* IMPORTANT: must be called with RCU read lock taken. */ #define for_each_page_ext (__page, __pgcount, __page_ext, __iter) \ - for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page));\ - __page_ext && __iter.index < __pgcount; \ - __page_ext = page_ext_iter_next(&__iter)) + for (__page_ext = page_ext_iter_begin(&__iter, page_to_pfn(__page), __pgcount); \ + __page_ext; \ + __page_ext = page_ext_iter_next(&__iter, __pgcount))#else /* !CONFIG_PAGE_EXTENSION */ struct page_ext ;
Greg Kroah-Hartman
maintainerPATCH 426/518 mm: do file ownership checks with the proper mount idmap
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pedro Falcato <[email protected]>
commit e187bc02f8fa4226d62814592cf064ee4557c470 upstream.
Ever since idmapped mounts were introduced, inode ownership checks (for side-channel protection) in mincore() and madvise(MADV_PAGEOUT) were done against the nop_mnt_idmap, which completely ignores the file's mount's idmap. This results in odd edgecases like:
1) mount/bind-mount with an idmap userA:userB:1
2) userB runs an owner_or_capable() check on file that is owned by userA on-disk/in-memory, but owned by userB after idmap translation
3) owner_or_capable() mysteriously fails as the correct idmap wasn't supplied
In the case of mincore/madvise MADV_PAGEOUT, this is usually benign, because file_permission(file, MAY_WRITE) will probably succeed, as it uses the proper idmap internally, but it does not need to be the case on e.g a 0444 file where even the owner itself doesn't have permissions to write to it.
Since this is clearly not trivial to get right, introduce a file_owner_or_capable() that can carry the correct semantics, and switch the various users in mm to it.
The issue was found by manual code inspection & an off-list discussion with Jan Kara.
--- include/linux/fs.h | 5 +++++ mm/filemap.c | 2 +- mm/madvise.c | 3 +-- mm/mincore.c | 3 +-- 4 files changed, 8 insertions(+), 5 deletions(-)
include/linux/fs.h +5 -0
@@ -2438,6 +2438,11 @@ static inline struct mnt_idmap *file_mntreturn mnt_idmap (file->f_path.mnt); } +static inline bool file_owner_or_capable (const struct file *file) +{ +return inode_owner_or_capable (file_mnt_idmap (file),file_inode (file)); +} +/** * is_idmapped_mnt - check whether a mount is mapped * @mnt: the mount to check
mm/filemap.c +1 -1
@@ -4671,7 +4671,7 @@ static inline bool can_do_cachestat(stru {if (f->f_mode &FMODE_WRITE )return true; -if (inode_owner_or_capable (file_mnt_idmap (f),file_inode (f))) +if (file_owner_or_capable (f))return true;return file_permission (f,MAY_WRITE ) ==0 ; }
mm/madvise.c +1 -2
@@ -336,8 +336,7 @@ static inline bool can_do_file_pageout(s* otherwise we'd be including shared non-exclusive mappings, which * opens a side channel. */ -return inode_owner_or_capable (&nop_mnt_idmap, -file_inode (vma->vm_file)) || +return file_owner_or_capable (vma->vm_file) ||file_permission (vma->vm_file,MAY_WRITE ) ==0 ; }
mm/mincore.c +1 -2
@@ -227,8 +227,7 @@ static inline bool can_do_mincore(struct* for writing; otherwise we'd be including shared non-exclusive * mappings, which opens a side channel. */ -return inode_owner_or_capable (&nop_mnt_idmap, -file_inode (vma->vm_file)) || +return file_owner_or_capable (vma->vm_file) ||file_permission (vma->vm_file,MAY_WRITE ) ==0 ; }
Greg Kroah-Hartman
maintainerPATCH 427/518 selftests/mm: pagemap_ioctl: use the correct page size for transact_test()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zenghui Yu <[email protected]>
commit dccf636bf1e68c3fda92f0c9e1018ab7e0ac8b2c upstream.
There are several places in transact_test() where we use the hardcoded 0x1000 (4k) as page size, which is not always correct for architectures supporting multiple page sizes.
Switch to use the correct page size. Otherwise ./ksft_pagemap.sh on a 16k-page-size arm64 box fails with
$ ./ksft_pagemap.sh [...] # ok 96 mprotect_tests Both pages written after remap and mprotect # ok 97 mprotect_tests Clear and make the pages written # Bail out! ioctl failed # # Planned tests != run tests (117 != 97) # # Totals: pass:97 fail:0 xfail:0 xpass:0 skip:0 error:0 # [FAIL] not ok 1 pagemap_ioctl # exit=1 # SUMMARY: PASS=0 SKIP=0 FAIL=1 1..1
--- tools/testing/selftests/mm/pagemap_ioctl.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
tools/testing/selftests/mm/pagemap_ioctl.c +6 -6
@@ -1366,7 +1366,7 @@ void *thread_proc(void *mem)ksft_exit_fail_msg ("pthread_barrier_wait\n" );for (i =0 ; i < access_per_thread; ++i) -__atomic_add_fetch (m + i * (0x1000 /sizeof (*m)),1 , __ATOMIC_SEQ_CST); +__atomic_add_fetch (m + i * (page_size /sizeof (*m)),1 , __ATOMIC_SEQ_CST); ret =pthread_barrier_wait (&end_barrier);if (ret && ret !=PTHREAD_BARRIER_SERIAL_THREAD )
@@ -1401,15 +1401,15 @@ static void transact_test(int page_size)if (pthread_barrier_init (&end_barrier,NULL , nthreads +1 ))ksft_exit_fail_msg ("pthread_barrier_init\n" ); - mem =mmap (NULL ,0x1000 * nthreads * pages_per_thread,PROT_READ |PROT_WRITE , + mem =mmap (NULL , page_size * nthreads * pages_per_thread,PROT_READ |PROT_WRITE ,MAP_ANONYMOUS |MAP_PRIVATE ,-1 ,0 );if (mem ==MAP_FAILED )ksft_exit_fail_msg ("Error mmap %s.\n" ,strerror (errno)); -wp_init (mem,0x1000 * nthreads * pages_per_thread); -wp_addr_range (mem,0x1000 * nthreads * pages_per_thread); +wp_init (mem, page_size * nthreads * pages_per_thread); +wp_addr_range (mem, page_size * nthreads * pages_per_thread); -memset (mem,0 ,0x1000 * nthreads * pages_per_thread); +memset (mem,0 , page_size * nthreads * pages_per_thread); count =get_dirty_pages_reset (mem, nthreads * pages_per_thread,1 , page_size);ksft_test_result (count >0 ,"%s count %u\n" , __func__, count);
@@ -1418,7 +1418,7 @@ static void transact_test(int page_size) finish =0 ;for (i =0 ; i < nthreads; ++i) -pthread_create (&th,NULL , thread_proc, mem +0x1000 * i * pages_per_thread); +pthread_create (&th,NULL , thread_proc, mem + page_size * i * pages_per_thread); extra_pages =0 ;for (i =0 ; i < iter_count; ++i) {
Greg Kroah-Hartman
maintainerPATCH 428/518 selftests/mm: fix ksft_process_madv.sh test category
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sarthak Sharma <[email protected]>
commit 4b0363cb1f3ec42b0b1346e5ab0b8a3dceeee9be upstream.
ksft_process_madv.sh currently runs run_vmtests.sh with the mmap category. Update it to run the process_madv category, since ksft_mmap.sh already runs the mmap category tests.
This avoids running mmap tests twice and ensures that process_madv tests are run through the kselftest harness.
--- tools/testing/selftests/mm/ksft_process_madv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/mm/ksft_process_madv.sh +1 -1
@@ -1,4 +1,4 @@ #!/bin/sh -e # SPDX-License-Identifier: GPL-2.0 -./run_vmtests.sh -t mmap +./run_vmtests.sh -t process_madv
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 429/518 nouveau/vmm: fix another SPT/LPT race
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dave Airlie <[email protected]>
commit 6763a0aea6d658d69b9215ab9151d7bd4c1c314b upstream.
We've had an unknown Turing issue for a while with page faults since large pages and compression.
I've got a patch series that syncs all our L2 handling with ogkm and it made this fault happen more.
After writing a bunch of debugging patches, I spotted an invalid LPT entry where there should have been a valid one.
A 64K MAP succeeds on a range, but a subsequent SPT put drops SPT refs across multiple ranges,
We shouldn't assume all ranges where SPTEs go away will have the same sparse/invalid/valid state, just iterate over each instead and do the right thing.
--- drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c | 31 +++++++++++--------------- 1 file changed, 14 insertions(+), 17 deletions(-)
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c +14 -17
@@ -230,29 +230,26 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_ite* covered by a number of LPTEs, the LPTEs once again take * control over their address range. * -* Determine how many LPTEs need to transition state. +* Transition each LPTE individually as each may have a +* different target state (sparse, invalid, or valid). */ - pgt->pte[ptei].s.spte_valid = false; -for (ptes =1 , ptei++; ptei < lpti; ptes++, ptei++) { +for (ptei++; ptei < lpti; ptei++) {if (pgt->pte[ptei].s.sptes)break ; - pgt->pte[ptei].s.spte_valid = false; } -if (pgt->pte[pteb].s.sparse) { -TRA (it,"LPTE %05x: U -> S %d PTEs" , pteb, ptes); - pair->func->sparse (vmm, pgt->pt[0 ], pteb, ptes); - }else if (!pgt->pte[pteb].s.lpte_valid) { -if (pair->func->invalid) { -/* If the MMU supports it, restore the LPTE to the -* INVALID state to tell the MMU there is no point -* trying to fetch the corresponding SPTEs. -*/ -TRA (it,"LPTE %05x: U -> I %d PTEs" , pteb, ptes); - pair->func->invalid (vmm, pgt->pt[0 ], pteb, ptes); +while (pteb < ptei) { + pgt->pte[pteb].s.spte_valid = false; +if (pgt->pte[pteb].s.sparse) { +TRA (it,"LPTE %05x: U -> S" , pteb); + pair->func->sparse (vmm, pgt->pt[0 ], pteb,1 ); + }else if (!pgt->pte[pteb].s.lpte_valid) { +if (pair->func->invalid) { +TRA (it,"LPTE %05x: U -> I" , pteb); + pair->func->invalid (vmm, pgt->pt[0 ], pteb,1 ); + } } - }else { -TRA (it,"LPTE %05x: V %d PTEs" , pteb, ptes); + pteb++; } } }
Greg Kroah-Hartman
maintainerPATCH 430/518 bpf: Reject BPF_MAP_TYPE_INODE_STORAGE creation if BPF LSM is uninitialized
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matt Bobrowski <[email protected]>
commit a6f0643e4f63cfaa0d5d4a69de4f132eac4b8fe4 upstream.
When CONFIG_BPF_LSM=y is set, BPF inode storage maps (BPF_MAP_TYPE_INODE_STORAGE) are compiled into the kernel. However, if the BPF LSM is not explicitly enabled at boot time (e.g. omitted from the "lsm=" boot parameter), lsm_prepare() is never executed for the BPF LSM.
Consequently, the BPF inode security blob offset (bpf_lsm_blob_sizes.lbs_inode) is never initialized and remains at its default compiled size of 8 bytes instead of being updated to a valid offset past the reserved struct rcu_head (typically 16 bytes or more).
When a privileged user creates and updates a BPF_MAP_TYPE_INODE_STORAGE map, bpf_inode() evaluates inode->i_security + 8. This erroneously aliases the struct rcu_head.func callback pointer at the beginning of the inode->i_security blob. During subsequent map element cleanup or inode destruction, writing NULL to owner_storage clears the queued RCU callback pointer. When rcu_do_batch() later executes the queued callback, it attempts an instruction fetch at address 0x0, triggering an immediate kernel panic.
Fix this by introducing a global bpf_lsm_initialized boolean flag marked with __ro_after_init. Set this flag to true inside bpf_lsm_init() when the LSM framework successfully registers the BPF LSM. Gate map allocation in inode_storage_map_alloc() on this flag, returning -EOPNOTSUPP if the BPF LSM is in turn uninitialized.
This fail-fast approach prevents userspace from allocating inode storage maps when the supporting BPF LSM infrastructure is absent, avoiding zombie map states.
--- include/linux/bpf_lsm.h | 4 ++++ kernel/bpf/bpf_inode_storage.c | 9 +++++++++ security/bpf/hooks.c | 3 +++ 3 files changed, 16 insertions(+)
include/linux/bpf_lsm.h +4 -0
@@ -14,6 +14,8 @@#ifdef CONFIG_BPF_LSM +extern bool bpf_lsm_initialized__ro_after_init ; +#define LSM_HOOK (RET ,DEFAULT ,NAME , ...) \ RET bpf_lsm_##NAME(__VA_ARGS__);#include <linux/lsm_hook_defs.h>
@@ -55,6 +57,8 @@ bool bpf_lsm_has_d_inode_locked(const st#else /* !CONFIG_BPF_LSM */ +#define bpf_lsm_initialized false +static inline bool bpf_lsm_is_sleepable_hook (u32 btf_id) {return false;
kernel/bpf/bpf_inode_storage.c +9 -0
@@ -178,6 +178,15 @@ static int notsupp_get_next_key(struct bstatic struct bpf_map *inode_storage_map_alloc (union bpf_attr *attr) { +/* +* Do not allow allocation of BPF_MAP_TYPE_INODE_STORAGE if the BPF LSM +* was not initialized by the LSM framework at boot. Without proper +* initialization, the BPF inode security blob offset remains unprepared, +* causing bpf_inode() to calculate an invalid memory offset and corrupt +* inode->i_security. +*/ +if (!bpf_lsm_initialized) +return ERR_PTR (-EOPNOTSUPP );return bpf_local_storage_map_alloc (attr, &inode_cache); }
security/bpf/hooks.c +3 -0
@@ -7,6 +7,8 @@#include <linux/bpf_lsm.h> #include <uapi/linux/lsm.h> +bool bpf_lsm_initialized__ro_after_init ; +static struct security_hook_list bpf_lsm_hooks[]__ro_after_init = { #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
@@ -24,6 +26,7 @@ static int __init bpf_lsm_init(void) {security_add_hooks (bpf_lsm_hooks,ARRAY_SIZE (bpf_lsm_hooks), &bpf_lsmid); + bpf_lsm_initialized = true;pr_info ("LSM support for eBPF active\n" );return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 431/518 iommu/vt-d: Avoid WARNING in sva unbind path
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Lu Baolu <[email protected]>
commit 534b5f98ab7319d8004bbc7dab6481462243e883 upstream.
The Intel IOMMU driver allows SVA on devices even if they do not support PCI/PRI. Commit 39c20c4e83b9 ("iommu/vt-d: Only handle IOPF for SVA when PRI is supported") modified the SVA bind path to allow this configuration by skipping IOPF enablement when PRI is missing. However, it failed to update the unbind path.
This creates an imbalance: the unbind path attempts to disable IOPF for a device that never had it enabled, triggering a WARNING in intel_iommu_disable_iopf():
WARNING: drivers/iommu/intel/iommu.c:3475 at intel_iommu_disable_iopf+0x4f/0x90d Call Trace: <TASK> blocking_domain_set_dev_pasid+0x50/0x70 iommu_detach_device_pasid+0x89/0xc0 iommu_sva_unbind_device+0x73/0x150 xe_vm_close_and_put+0x4d2/0x1200 [xe]
Fix this by bypassing IOPF operations for SVA domains on non-PRI hardware in both the bind and unbind paths.
--- drivers/iommu/intel/iommu.h | 11 +++++++++++ drivers/iommu/intel/svm.c | 12 ++++-------- 2 files changed, 15 insertions(+), 8 deletions(-)
drivers/iommu/intel/iommu.h +11 -0
@@ -1254,18 +1254,29 @@ void intel_iommu_disable_iopf(struct devstatic inline int iopf_for_domain_set (struct iommu_domain *domain,struct device *dev) { +struct device_domain_info *info =dev_iommu_priv_get (dev); +if (!domain || !domain->iopf_handler)return 0 ; +/* SVA with non-IOMMU/PRI IOPF handling is allowed. */ +if (domain->type ==IOMMU_DOMAIN_SVA && !info->pri_supported) +return 0 ; +return intel_iommu_enable_iopf (dev); }static inline void iopf_for_domain_remove (struct iommu_domain *domain,struct device *dev) { +struct device_domain_info *info =dev_iommu_priv_get (dev); +if (!domain || !domain->iopf_handler)return ; +if (domain->type ==IOMMU_DOMAIN_SVA && !info->pri_supported) +return ; +intel_iommu_disable_iopf (dev); }
drivers/iommu/intel/svm.c +4 -8
@@ -164,12 +164,9 @@ static int intel_svm_set_dev_pasid(strucif (IS_ERR (dev_pasid))return PTR_ERR (dev_pasid); -/* SVA with non-IOMMU/PRI IOPF handling is allowed. */ -if (info->pri_supported) { - ret =iopf_for_domain_replace (domain, old, dev); -if (ret) - goto out_remove_dev_pasid; - } + ret =iopf_for_domain_replace (domain, old, dev); +if (ret) + goto out_remove_dev_pasid;/* Setup the pasid table: */ sflags =cpu_feature_enabled (X86_FEATURE_LA57 ) ?PASID_FLAG_FL5LP :0 ;
@@ -184,8 +181,7 @@ static int intel_svm_set_dev_pasid(strucreturn 0 ; out_unwind_iopf: -if (info->pri_supported) -iopf_for_domain_replace (old, domain, dev); +iopf_for_domain_replace (old, domain, dev); out_remove_dev_pasid:domain_remove_dev_pasid (domain, dev, pasid);return ret;
Greg Kroah-Hartman
maintainerPATCH 432/518 iommu/amd: Dont split flush for amd_iommu_domain_flush_all()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Weinan Liu <[email protected]>
commit 69fe699afe1afcb730164b86c228483c2da05f94 upstream.
We have observed multiple full invalidations occurring during device detach when we are done using the vfio-device.
blocked_domain_attach_device()
-> detach_device()
-> amd_iommu_domain_flush_all()
-> amd_iommu_domain_flush_pages(..., CMD_INV_IOMMU_ALL_PAGES_ADDRESS)
while (size != 0) {
-> __domain_flush_pages( flush_size /* power of 2 flush_size */)
-> domain_flush_pages_v1()
-> build_inv_iommu_pages()
-> build_inv_address()
}build_inv_address() will trigger a full invalidation if the chunk size > (1 << 51). Consequently, the guest will issue multiple full invalidations for a single call to amd_iommu_domain_flush_all()
Without this patch, we will see 10 time instead of 1 time full invalidations for every amd_iommu_domain_flush_all().
--- drivers/iommu/amd/iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/iommu/amd/iommu.c +2 -1
@@ -1774,7 +1774,8 @@ void amd_iommu_domain_flush_pages(struct {lockdep_assert_held (&domain->lock); -if (likely (!amd_iommu_np_cache)) { +if (likely (!amd_iommu_np_cache) || + size >= (1ULL <<52 )) {__domain_flush_pages (domain, address, size);/* Wait until IOMMU TLB and all device IOTLB flushes are complete */
Greg Kroah-Hartman
maintainerPATCH 433/518 iommufd: Use sizeof(*hdr) instead of sizeof(hdr) in veventq read
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kai Aizen <[email protected]>
commit be93d186ae88a92e7aa77e122d4e661fa57b1e39 upstream.
The bound-check in iommufd_veventq_fops_read() for the normal vEVENT path uses sizeof(hdr) where the surrounding code uses sizeof(*hdr):
if (!vevent_for_lost_events_header(cur) &&
sizeof(hdr) + cur->data_len > count - done) {hdr is declared as struct iommufd_vevent_header *, so sizeof(hdr) evaluates to the size of the pointer. Surrounding code uses sizeof(*hdr) consistently:
if (done >= count || sizeof(*hdr) > count - done) {
...
if (copy_to_user(buf + done, hdr, sizeof(*hdr))) {
...
done += sizeof(*hdr);struct iommufd_vevent_header is currently 8 bytes (two __u32 fields, flags and sequence), so on 64-bit (sizeof(void *) == 8) the two expressions happen to be equal and the check works as intended.
On 32-bit (sizeof(void *) == 4) the check under-counts the header by 4 bytes: a vEVENT whose data_len causes 8 + cur->data_len to exceed count - done while 4 + cur->data_len does not will pass the check, then the loop will copy_to_user 8 bytes of header followed by data_len bytes of payload, writing past the user-supplied buffer.
It is also a latent bug for any future expansion of struct iommufd_vevent_header beyond sizeof(void *) on 64-bit; the check should not depend on the type happening to match the host pointer width.
Use sizeof(*hdr) to match the rest of the function and the actual amount that will be copied.
--- drivers/iommu/iommufd/eventq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/iommufd/eventq.c +1 -1
@@ -321,7 +321,7 @@ static ssize_t iommufd_veventq_fops_read/* If being a normal vEVENT, validate against the full size */ if (!vevent_for_lost_events_header (cur) && -sizeof (hdr) + cur->data_len > count - done) { +sizeof (*hdr) + cur->data_len > count - done) {iommufd_veventq_deliver_restore (veventq, cur);break ; }
Greg Kroah-Hartman
maintainerPATCH 434/518 iommufd: Fix data_len byte-count vs element-count mismatch
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 85345becfead3255a5f875d4b4d82ea01d926239 upstream.
kzalloc_flex() computes the allocation size. With event_data typed as u64, data_len is interpreted as a u64 element count. Yet, every caller and the read path treat data_len as a byte count. The current code over-allocates by sizeof(u64) and the __counted_by() annotation overstates the length by the same factor.
Re-type event_data as u8. No functional change in user-visible behavior.
--- drivers/iommu/iommufd/iommufd_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/iommu/iommufd/iommufd_private.h +1 -1
@@ -602,7 +602,7 @@ struct iommufd_vevent {struct iommufd_vevent_header header;struct list_head node;/* for iommufd_eventq::deliver */ ssize_t data_len; - u64 event_data[] __counted_by(data_len); + u8 event_data[] __counted_by(data_len); };#define vevent_for_lost_events_header (vevent) \
Greg Kroah-Hartman
maintainerPATCH 435/518 iommufd: Move vevent memory allocation outside spinlock
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 47443565d10c51366c9382dbc8597cd6c460b8a2 upstream.
The veventq memory allocation happens inside the spinlock. Given its depth is decided by the user space, this leaves a vulnerability, where userspace can allocate large queues to exhaust atomic memory reserves.
Move the allocation outside the spinlock and use GFP_NOWAIT, which can fail fast under memory pressure without dipping into the GFP_ATOMIC reserves or direct-reclaiming from the threaded IRQ handler. On allocation failure, queue the lost_events_header (so userspace learns of the drop) and return -ENOMEM so the caller learns of the kernel-side memory pressure.
This is intentionally distinct from the queue-overflow path, which also queues the lost_events_header but returns 0: a full queue is an expected userspace-pacing condition rather than a kernel error.
A subsequent change will cap the upper bound of the veventq_depth.
--- drivers/iommu/iommufd/driver.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
drivers/iommu/iommufd/driver.c +8 -5
@@ -149,15 +149,18 @@ int iommufd_viommu_report_event(struct i goto out_unlock_veventqs; } -spin_lock (&veventq->common.lock); -if (veventq->num_events == veventq->depth) { +/* Pre-allocate to avoid GFP_ATOMIC; use GFP_NOWAIT to avoid sleeping */ + vevent =kzalloc_flex (*vevent, event_data, data_len,GFP_NOWAIT ); +if (!vevent) { +spin_lock (&veventq->common.lock); vevent = &veventq->lost_events_header; + rc = -ENOMEM ; goto out_set_header; } - vevent =kzalloc_flex (*vevent, event_data, data_len,GFP_ATOMIC ); -if (!vevent) { - rc = -ENOMEM ; +spin_lock (&veventq->common.lock); +if (veventq->num_events == veventq->depth) { +kfree (vevent); vevent = &veventq->lost_events_header; goto out_set_header; }
Greg Kroah-Hartman
maintainerPATCH 436/518 iommufd: Set veventq_depth upper bound
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 6ebf2eb46fbd5b40393ff8fbb847ba96925beaff upstream.
iommufd_veventq_alloc() accepts any !0 veventq_depth from userspace, with an upper bound at U32_MAX.
This leaves a vulnerability where userspace can allocate excessively large queues to exhaust kernel memory reserves.
Cap the veventq_depth (maximum number of entries) to 1 << 19, matching the maximum number of entries in the SMMUv3 EVTQ (the largest use case today).
--- drivers/iommu/iommufd/eventq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/iommu/iommufd/eventq.c +4 -1
@@ -473,6 +473,9 @@ int iommufd_fault_iopf_handler(struct iostatic const struct file_operations iommufd_veventq_fops =INIT_EVENTQ_FOPS (iommufd_veventq_fops_read,NULL ); +/* An arbitrary upper bound for veventq_depth that fits all existing HWs */ +#define VEVENTQ_MAX_DEPTH (1U << 19) +int iommufd_veventq_alloc (struct iommufd_ucmd *ucmd) {struct iommu_veventq_alloc *cmd = ucmd->cmd;
@@ -484,7 +487,7 @@ int iommufd_veventq_alloc(struct iommufdif (cmd->flags || cmd->__reserved || cmd->type ==IOMMU_VEVENTQ_TYPE_DEFAULT )return -EOPNOTSUPP ; -if (!cmd->veventq_depth) +if (!cmd->veventq_depth || cmd->veventq_depth >VEVENTQ_MAX_DEPTH )return -EINVAL ; viommu =iommufd_get_viommu (ucmd, cmd->viommu_id);
Greg Kroah-Hartman
maintainerPATCH 437/518 iommufd: Rewind header length in done if iommufd_veventq_fops_read() fails
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 01e41ad76c12ae5c49ab4ef4fc7dd54e9b8784d6 upstream.
When the first event copy fails, rc = -EFAULT will not be reported as done is set to the length of the copied header.
Rewind it to report rc correctly.
--- drivers/iommu/iommufd/eventq.c | 1 + 1 file changed, 1 insertion(+)
drivers/iommu/iommufd/eventq.c +1 -0
@@ -336,6 +336,7 @@ static ssize_t iommufd_veventq_fops_readif (cur->data_len &©_to_user (buf + done, cur->event_data, cur->data_len)) {iommufd_veventq_deliver_restore (veventq, cur); + done -=sizeof (*hdr); rc = -EFAULT ;break ; }
Greg Kroah-Hartman
maintainerPATCH 438/518 iommufd: Reject invalid read count in iommufd_veventq_fops_read()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 00203ca8323f9714630408c19a209b52397975e6 upstream.
The read count must be large enough to hold a vEVENT header. For a normal vEVENT, it must also hold the trailing data following the header.
iommufd_veventq_fops_read() does not validate the count, but returns 0 as if the read had succeeded while leaving the pending event in the queue.
Return -EINVAL in both undersize cases.
--- drivers/iommu/iommufd/eventq.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/iommu/iommufd/eventq.c +6 -0
@@ -310,6 +310,9 @@ static ssize_t iommufd_veventq_fops_readif (*ppos)return -ESPIPE ; +/* Minimum read count is a vEVENT header */ +if (count <sizeof (*hdr)) +return -EINVAL ;while ((cur =iommufd_veventq_deliver_fetch (veventq))) {/* Validate the remaining bytes against the header size */
@@ -323,6 +326,9 @@ static ssize_t iommufd_veventq_fops_readif (!vevent_for_lost_events_header (cur) &&sizeof (*hdr) + cur->data_len > count - done) {iommufd_veventq_deliver_restore (veventq, cur); +/* Read count doesn't fit a single normal vEVENT */ +if (done ==0 ) + rc = -EINVAL ;break ; }
Greg Kroah-Hartman
maintainerPATCH 439/518 iommufd: Propagate allocation failure in iommufd_veventq_deliver_fetch()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 489e63dd120bad52eba63f5506c214750cd5bc75 upstream.
When the kzalloc_obj() fails in iommufd_veventq_deliver_fetch(), it returns NULL, falsely advertising to userspace that the queue is empty.
Propagate the -ENOMEM properly to the caller.
--- drivers/iommu/iommufd/eventq.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/iommu/iommufd/eventq.c +9 -1
@@ -264,8 +264,10 @@ iommufd_veventq_deliver_fetch(struct iom/* Make a copy of the lost_events_header for copy_to_user */ if (next == &veventq->lost_events_header) { vevent =kzalloc_obj (*vevent,GFP_ATOMIC ); -if (!vevent) +if (!vevent) { + vevent =ERR_PTR (-ENOMEM ); goto out_unlock; + } }list_del (&next->node);if (vevent)
@@ -315,6 +317,12 @@ static ssize_t iommufd_veventq_fops_readreturn -EINVAL ;while ((cur =iommufd_veventq_deliver_fetch (veventq))) { +if (IS_ERR (cur)) { +if (done ==0 ) + rc =PTR_ERR (cur); +break ; + } +/* Validate the remaining bytes against the header size */ if (done >= count ||sizeof (*hdr) > count - done) {iommufd_veventq_deliver_restore (veventq, cur);
Greg Kroah-Hartman
maintainerPATCH 440/518 iommufd: Reject invalid read count in iommufd_fault_fops_read()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 47916a54eeb2a9e654512ee609f71bd5b29db702 upstream.
The read count must be large enough to hold one fault or a group's faults.
iommufd_fault_fops_read() does not validate the count, but returns 0 as if the read had succeeded while leaving the pending fault in the queue.
Return -EINVAL in the undersize cases.
--- drivers/iommu/iommufd/eventq.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/iommu/iommufd/eventq.c +3 -0
@@ -142,6 +142,9 @@ static ssize_t iommufd_fault_fops_read(sif (done >= count || group->fault_count * fault_size > count - done) {iommufd_fault_deliver_restore (fault, group); +/* Read count doesn't fit the first fault group */ +if (done ==0 ) + rc = -EINVAL ;break ; }
Greg Kroah-Hartman
maintainerPATCH 441/518 iommufd: Break the loop on failure in iommufd_fault_fops_read()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 172fc8b19825a0f5884c38f2289188284e2d45ee upstream.
On a copy_to_user() failure inside the inner list_for_each_entry, only the inner loop breaks; the outer while re-fetches the just-restored fault group and retries the failing copy_to_user() forever, spinning the reader at 100% CPU with fault->mutex held.
Check rc after the inner loop and break the outer while as well.
--- drivers/iommu/iommufd/eventq.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/iommu/iommufd/eventq.c +2 -0
@@ -168,6 +168,8 @@ static ssize_t iommufd_fault_fops_read(s } done += fault_size; } +if (rc) +break ; }mutex_unlock (&fault->mutex);
Greg Kroah-Hartman
maintainerPATCH 442/518 iommufd: Avoid partial fault group delivery in iommufd_fault_fops_read()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 091ab6d70dc444f56ed14faedbcacfc979f4c613 upstream.
The cookie returned by xa_alloc() in iommufd_fault_fops_read() is per fault group, but the inner copy_to_user() runs per fault inside the group. If a copy fails mid-group, xa_erase clears the cookie and the group is restored to the deliver list, yet done is not rolled back. The function returns the partial byte count, with the successfully copied faults sitting at offsets below done carrying the now-erased cookie. The next read() then re-fetches the group, allocates a fresh cookie, and re-delivers every fault including the ones already copied; userspace sees duplicates carrying the new cookie, and a stale cookie that can never be responded to.
Use a local group_done variable that tracks the per-group progress inside the inner loop, and only commit done = group_done after the inner loop has finished successfully. On a copy_to_user failure the outer break skips the commit, so done remains at its prior start-of-group baseline; the partial bytes already written past done are undefined to userspace per the read(2) contract, and the next read re-delivers the whole group atomically.
--- drivers/iommu/iommufd/eventq.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/iommu/iommufd/eventq.c +5 -2
@@ -139,6 +139,8 @@ static ssize_t iommufd_fault_fops_read(smutex_lock (&fault->mutex);while ((group =iommufd_fault_deliver_fetch (fault))) { +size_t group_done = done; +if (done >= count || group->fault_count * fault_size > count - done) {iommufd_fault_deliver_restore (fault, group);
@@ -160,16 +162,17 @@ static ssize_t iommufd_fault_fops_read(siommufd_compose_fault_message (&iopf->fault, &data, idev, group->cookie); -if (copy_to_user (buf + done, &data, fault_size)) { +if (copy_to_user (buf + group_done, &data, fault_size)) {xa_erase (&fault->response, group->cookie);iommufd_fault_deliver_restore (fault, group); rc = -EFAULT ;break ; } - done += fault_size; + group_done += fault_size; }if (rc)break ; + done = group_done; }mutex_unlock (&fault->mutex);
Greg Kroah-Hartman
maintainerPATCH 443/518 iommufd: Set upper bounds on cache invalidation entry_num and entry_len
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nicolin Chen <[email protected]>
commit 4d70986002f2f3eaaed89124fb2522bded38b016 upstream.
iommufd_hwpt_invalidate() takes a user-controlled entry_num and entry_len, each bounded only by U32_MAX. An entry_len beyond the kernel's struct size makes the copy helper verify the extra bytes are zero, scanning that excess in one uninterruptible pass; a multi-gigabyte value over zeroed user memory trips the soft-lockup watchdog.
A large entry_num is the other half, driving the backend invalidation loop with no reschedule. The VT-d nested handler, for one, copies each entry and flushes caches per iteration, pinning the CPU on a non-preemptible kernel.
Cap both in the ioctl. entry_len is held under PAGE_SIZE, above any request struct, and entry_num under 1 << 19, the order of a hardware invalidation queue and well beyond any real batch, bounding the per-call loop length.
--- drivers/iommu/iommufd/hw_pagetable.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/iommu/iommufd/hw_pagetable.c +10 -1
@@ -489,6 +489,9 @@ int iommufd_hwpt_get_dirty_bitmap(structreturn rc; } +/* An arbitrary entry_num cap, far above any realistic invalidation batch */ +#define IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX (1U << 19) +int iommufd_hwpt_invalidate (struct iommufd_ucmd *ucmd) {struct iommu_hwpt_invalidate *cmd = ucmd->cmd;
@@ -507,7 +510,13 @@ int iommufd_hwpt_invalidate(struct iommu goto out; } -if (cmd->entry_num && (!cmd->data_uptr || !cmd->entry_len)) { +/* +* Bound entry_num and entry_len so a single call cannot pin the CPU; +* entry_len also caps the copy_struct_from_user() trailing-zero scan. +*/ +if (cmd->entry_num && + (!cmd->data_uptr || !cmd->entry_len || cmd->entry_len >PAGE_SIZE || + cmd->entry_num >IOMMU_HWPT_INVALIDATE_ENTRY_NUM_MAX )) { rc = -EINVAL ; goto out; }
Greg Kroah-Hartman
maintainerPATCH 444/518 audit: fix removal of dangling executable rules
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ricardo Robaina <[email protected]>
commit 888a0396e154524f4027f27da84bdbec9eb68916 upstream.
When an audited executable is deleted from the disk, its dentry becomes negative. Any later attempt to delete the associated audit rule will lead to audit_alloc_mark() encountering this negative dentry and immediately aborting, returning -ENOENT.
This early abort prevents the subsystem from allocating the temporary fsnotify mark needed to construct the search key, meaning the kernel cannot find the existing rule in its own lists to delete it. This leaves a dangling rule in memory, resulting in the following error while attempting to delete the rule:
# ./audit-dupe-exe-deadlock.sh No rules Error deleting rule (No such file or directory) There was an error while processing parameters
# auditctl -l -a always,exit -S all -F exe=/tmp/file -F path=/tmp/file -F key=dr
# auditctl -D Error deleting rule (No such file or directory) There was an error while processing parameters
This patch fixes this issue by removing the d_really_is_negative() check. By doing so, a dummy mark can be successfully generated for the deleted path, which allows the audit subsystem to properly match and flush the dangling rule.
--- kernel/audit_fsnotify.c | 4 ---- 1 file changed, 4 deletions(-)
kernel/audit_fsnotify.c +0 -4
@@ -84,10 +84,6 @@ struct audit_fsnotify_mark *audit_alloc_ dentry =kern_path_parent (pathname, &path);if (IS_ERR (dentry))return ERR_CAST (dentry);/* returning an error */ -if (d_really_is_negative (dentry)) { - audit_mark =ERR_PTR (-ENOENT ); - goto out; - } audit_mark =kzalloc_obj (*audit_mark);if (unlikely (!audit_mark)) {
Greg Kroah-Hartman
maintainerPATCH 445/518 landlock: Set audit_net.sk for socket access checks
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mickaël Salaün <[email protected]>
commit d936e1a9170f9cadaa5f37586b1dfe6f20f98799 upstream.
Set audit_net.sk in current_check_access_socket() to provide the socket object to audit_log_lsm_data(). This makes Landlock consistent with AppArmor, which always sets .sk for socket operations, and with SELinux's generic socket permission checks.
The socket's local and foreign address information (laddr, lport, faddr, fport) is logged by the shared lsm_audit.c infrastructure when the socket has bound or connected state. Fields with zero values are suppressed by print_ipv4_addr()/print_ipv6_addr(), so the audit output is unchanged for the common case of bind denials on unbound sockets. For connect denials after a prior bind, the bound local address (laddr, lport) appears before the existing sockaddr fields (daddr, dest).
No existing fields are removed or reordered, and the new field names (laddr, lport, faddr, fport) are standard audit fields already emitted by other LSMs through the same lsm_audit.c code path.
Add a connect_tcp_bound audit test that binds to an allowed port and then connects to a denied one, verifying that the denial record reports laddr/lport from the bound socket in addition to the connect destination.
--- security/landlock/net.c | 1 tools/testing/selftests/landlock/net_test.c | 62 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
security/landlock/net.c +1 -0
@@ -198,6 +198,7 @@ static int current_check_access_socket(sreturn 0 ; audit_net.family = address->sa_family; + audit_net.sk = sock->sk; landlock_log_denial(subject, &(struct landlock_request){ .type = LANDLOCK_REQUEST_NET_ACCESS,
tools/testing/selftests/landlock/net_test.c +62 -0
66 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 446/518 selftests/landlock: Explicitly disable audit in teardowns
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maximilian Heyne <[email protected]>
commit 0302cd72fe196aee933e3fb76f6d175d1ab0e843 upstream.
I'm seeing sporadic selftest failures, such as
# RUN scoped_audit.connect_to_child ...
# scoped_abstract_unix_test.c:314:connect_to_child:Expected 0 (0) == records.access (8) # connect_to_child: Test failed
# FAIL scoped_audit.connect_to_child
not ok 19 scoped_audit.connect_to_child
This seems similar to what commit 3647a4977fb73d ("selftests/landlock:
Drain stale audit records on init") tried to fix. However, the added drain loop is not effective. When setting the AUDIT_STATUS_PID, the kauditd_thread is woken up starting to send messages from the hold queue to the netlink. Depending on scheduling of this kthread not all messages might be send via the netlink in the 1 us interval.
Therefore, instead of trying to drain the queue, let's just disable audit when running non-audit tests or more precisely disable it after audit-tests. This way we won't generate any new audit message that could interfere with the other tests.
The comment saying that on process exit audit will be disabled is wrong. The closed file descriptor just causes an auditd_reset(), not a disablement. So future messages will be queued in the hold queue.
--- tools/testing/selftests/landlock/audit.h | 21 ++++++++------------- tools/testing/selftests/landlock/audit_test.c | 4 +--- 2 files changed, 9 insertions(+), 16 deletions(-)
tools/testing/selftests/landlock/audit.h +8 -13
@@ -494,10 +494,9 @@ static int audit_init_filter_exe(structstatic int audit_cleanup (int audit_fd,struct audit_filter *filter) {struct audit_filter new_filter; +int err =0 ;if (audit_fd <0 || !filter) { -int err; -/* * Simulates audit_init_with_exe_filter() when called from * FIXTURE_TEARDOWN_PARENT().
@@ -508,23 +507,19 @@ static int audit_cleanup(int audit_fd, s filter = &new_filter; err =audit_init_filter_exe (filter,NULL ); -if (err) { -close (audit_fd); -return err; - } +if (err) + goto err_close; }/* Filters might not be in place. */ audit_filter_exe (audit_fd, filter,AUDIT_DEL_RULE );audit_filter_drop (audit_fd,AUDIT_DEL_RULE ); -/* -* Because audit_cleanup() might not be called by the test auditd -* process, it might not be possible to explicitly set it. Anyway, -* AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd -* process will exit. -*/ -return close (audit_fd); + err =audit_set_status (audit_fd,AUDIT_STATUS_ENABLED ,0 ); + +err_close: +close (audit_fd); +return err; }static int audit_init_with_exe_filter (struct audit_filter *filter)
tools/testing/selftests/landlock/audit_test.c +1 -3
@@ -849,10 +849,8 @@ FIXTURE_SETUP(audit_exec)FIXTURE_TEARDOWN (audit_exec) {set_cap (_metadata,CAP_AUDIT_CONTROL ); -EXPECT_EQ (0 ,audit_filter_exe (self->audit_fd, &self->audit_filter, -AUDIT_DEL_RULE )); +EXPECT_EQ (0 ,audit_cleanup (self->audit_fd, &self->audit_filter));clear_cap (_metadata,CAP_AUDIT_CONTROL ); -EXPECT_EQ (0 ,close (self->audit_fd)); }TEST_F (audit_exec, signal_and_open)
Greg Kroah-Hartman
maintainerPATCH 447/518 landlock: Account all audit data allocations to user space
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mickaël Salaün <[email protected]>
commit b232bd12789fa57405b5092f28788be97aae9999 upstream.
Mark the kzalloc_flex() of struct landlock_details with GFP_KERNEL_ACCOUNT so the allocation is charged to the calling task, like the other Landlock per-domain allocations which have used GFP_KERNEL_ACCOUNT forever.
Every property of landlock_details is caller-attributable: allocated by landlock_restrict_self(2), owned by the caller's landlock_hierarchy, contents are the caller's pid, uid, comm, and exe_path, lifetime bounded by the caller's domain. While the caller may not know nor control the size of this allocation (i.e. exe_path), this data should still be accounted for it.
The deciding factor is whether userspace can trigger the allocation, not whether the size of the data is known nor controlled by the caller. This aligns with the kmemcg accounting policy established by commit 5d097056c9a0 ("kmemcg: account certain kmem allocations to memcg").
No new failure modes: the hierarchy and ruleset are allocated before details and are already accounted, so landlock_restrict_self(2) already returns -ENOMEM under memcg pressure. This change widens that existing failure window slightly; it does not introduce a new error code.
--- security/landlock/domain.c | 9 +++++---- security/landlock/domain.h | 5 +---- 2 files changed, 6 insertions(+), 8 deletions(-)
security/landlock/domain.c +5 -4
@@ -90,11 +90,12 @@ static struct landlock_details *get_currreturn ERR_CAST (buffer);/* -* Create the new details according to the path's length. Do not -* allocate with GFP_KERNEL_ACCOUNT because it is independent from the -* caller. +* Create the new details according to the path's length. Account to +* the calling task's memcg, like the other Landlock per-domain +* allocations, even if it may not control the related size. */ - details =kzalloc_flex (*details, exe_path, path_size); + details = +kzalloc_flex (*details, exe_path, path_size,GFP_KERNEL_ACCOUNT );if (!details)return ERR_PTR (-ENOMEM );
security/landlock/domain.h +1 -4
@@ -33,10 +33,7 @@ enum landlock_log_status {* Rarely accessed, mainly when logging the first domain's denial. * * The contained pointers are initialized at the domain creation time and never -* changed again. Contrary to most other Landlock object types, this one is -* not allocated with GFP_KERNEL_ACCOUNT because its size may not be under the -* caller's control (e.g. unknown exe_path) and the data is not explicitly -* requested nor used by tasks. +* changed again. */ struct landlock_details {/**
Greg Kroah-Hartman
maintainerPATCH 448/518 selftests/landlock: Filter dealloc records in audit_count_records()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mickaël Salaün <[email protected]>
commit 26679fad81a471428707d2dd7b0418204c52b7e4 upstream.
audit_count_records() counts both AUDIT_LANDLOCK_DOMAIN allocation and deallocation records in records.domain . Domain deallocation is tied to asynchronous credential freeing via kworker threads (landlock_put_ruleset_deferred), so the dealloc record can arrive after the drain in audit_init() and after the preceding audit_match_record() call. This causes flaky failures in tests that assert an exact records.domain count: a stale dealloc record from a previous test's domain inflates the count by one.
Observed on x86_64 under build configurations that delay the kworker firing the dealloc callback (e.g. coverage instrumentation): the audit_layout1 tests in fs_test.c intermittently saw records.domain == 2 where 1 was expected. The fix is in the shared helper, so those existing checks become robust without needing a fs_test.c edit.
Filter audit_count_records() with a regex to skip records containing deallocation status. The remaining domain records (allocation, emitted synchronously during landlock_log_denial()) are deterministic. Deallocation records are already tested explicitly via matches_log_domain_deallocated() in audit_test.c, which uses its own domain-ID-based filtering and longer timeout.
With this filter in place, re-add the records.domain == 0 checks that were removed in commit 3647a4977fb7 ("selftests/landlock: Drain stale audit records on init") as a workaround for this race.
Cc: Günther Noack <[email protected]>
Cc: [email protected]
Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()")
--- tools/testing/selftests/landlock/audit.h | 39 +++++++---- tools/testing/selftests/landlock/audit_test.c | 2 tools/testing/selftests/landlock/ptrace_test.c | 1 tools/testing/selftests/landlock/scoped_abstract_unix_test.c | 1 4 files changed, 30 insertions(+), 13 deletions(-)
tools/testing/selftests/landlock/audit.h +26 -13
@@ -381,18 +381,24 @@ struct audit_records { };/* -* WARNING: Do not assert records.domain == 0 without a preceding -* audit_match_record() call. Domain deallocation records are emitted -* asynchronously from kworker threads and can arrive after the drain in -* audit_init(), corrupting the domain count. A preceding audit_match_record() -* call consumes stale records while scanning, making the assertion safe in -* practice because stale deallocation records arrive before the expected access -* records. +* Counts remaining audit records by type, skipping domain deallocation records. +* Deallocation records are emitted asynchronously from kworker threads after a +* previous test's child has exited, so they can arrive after the drain in +* audit_init() and after the preceding audit_match_record() call. Allocation +* records are emitted synchronously during landlock_log_denial() in the current +* test's syscall context, so only those are counted in records->domain. */ static int audit_count_records (int audit_fd,struct audit_records *records) { +static const char dealloc_pattern[] =REGEX_LANDLOCK_PREFIX +" status=deallocated " ;struct audit_message msg; -int err; +regex_t dealloc_re; +int ret, err =0 ; + + ret =regcomp (&dealloc_re, dealloc_pattern,0 ); +if (ret) +return -ENOMEM ; records->access =0 ; records->domain =0 ;
@@ -402,9 +408,8 @@ static int audit_count_records(int audit err =audit_recv (audit_fd, &msg);if (err) {if (err == -EAGAIN ) -return 0 ; -else -return err; + err =0 ; +break ; }switch (msg.header.nlmsg_type) {
@@ -412,12 +417,20 @@ static int audit_count_records(int audit records->access++;break ;case AUDIT_LANDLOCK_DOMAIN : - records->domain++; + ret =regexec (&dealloc_re, msg.data,0 ,NULL ,0 ); +if (ret ==REG_NOMATCH ) { + records->domain++; + }else if (ret !=0 ) { + err = -EIO ; + goto out; + }break ; } }while (true); -return 0 ; +out: +regfree (&dealloc_re); +return err; }static int audit_init (void )
tools/testing/selftests/landlock/audit_test.c +2 -0
@@ -730,6 +730,7 @@ TEST_F(audit_flags, signal) }else {EXPECT_EQ (1 , records.access); } +EXPECT_EQ (0 , records.domain);/* Updates filter rules to match the drop record. */ set_cap (_metadata,CAP_AUDIT_CONTROL );
@@ -915,6 +916,7 @@ TEST_F(audit_exec, signal_and_open)/* Tests that there was no denial until now. */ EXPECT_EQ (0 ,audit_count_records (self->audit_fd, &records));EXPECT_EQ (0 , records.access); +EXPECT_EQ (0 , records.domain);/* * Wait for the child to do a first denied action by layer1 and
tools/testing/selftests/landlock/ptrace_test.c +1 -0
@@ -342,6 +342,7 @@ TEST_F(audit, trace)/* Makes sure there is no superfluous logged records. */ EXPECT_EQ (0 ,audit_count_records (self->audit_fd, &records));EXPECT_EQ (0 , records.access); +EXPECT_EQ (0 , records.domain); yama_ptrace_scope =get_yama_ptrace_scope ();ASSERT_LE (0 , yama_ptrace_scope);
tools/testing/selftests/landlock/scoped_abstract_unix_test.c +1 -0
@@ -312,6 +312,7 @@ TEST_F(scoped_audit, connect_to_child)/* Makes sure there is no superfluous logged records. */ EXPECT_EQ (0 ,audit_count_records (self->audit_fd, &records));EXPECT_EQ (0 , records.access); +EXPECT_EQ (0 , records.domain);ASSERT_EQ (0 ,pipe2 (pipe_child,O_CLOEXEC ));ASSERT_EQ (0 ,pipe2 (pipe_parent,O_CLOEXEC ));
Greg Kroah-Hartman
maintainerPATCH 449/518 selftests/landlock: Increase default audit socket timeout
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mickaël Salaün <[email protected]>
commit d8dfb4c7faa87c3e41a8678f38f136c2c7c036fa upstream.
matches_log_fs() and other audit_match_record() callers intermittently return -EAGAIN under heavy debug configs (KASAN, lockdep). The audit record delivery pipeline is asynchronous: landlock_log_denial() queues the record to audit_queue, and kauditd_thread dequeues and delivers via netlink. Under debug configs, kauditd scheduling between audit_log_end() and netlink_unicast() can exceed a syscall round trip (more than 1 usec), which was the value of the socket timeout used for the recvfrom() calls.
The observed failure [1] is an EAGAIN error code (-11) which means that the access record had not arrived within the 1 usec timeout of recvfrom(). The expected record does arrive, but only after matches_log_fs() has already returned. It is then consumed by a later audit_count_records() call, making records.access == 1 instead of 0.
Switch the default socket timeout to the slow value (1 second) so all audit_match_record() callers wait long enough for kauditd delivery, and lower it to the fast value (1 usec) only on the two paths that expect no
record: audit_count_records() and the expected_domain_id == 0 probe in matches_log_domain_deallocated(). audit_init() drains stale records with the fast timeout (terminating on -EAGAIN once the backlog is empty) and switches to the patient default before returning. 1 second gives ~10x margin over the observed maximum (~100 ms, while the happy path is ~23 us).
Rename the timeval constants to reflect their new roles:
- audit_tv_dom_drop (1 second) -> audit_tv_default: default socket timeout, patient enough for asynchronous kauditd delivery.
- audit_tv_default (1 usec) -> audit_tv_fast: fast timeout for paths that expect no record (drain, audit_count_records(), probes).
Invert the conditional in matches_log_domain_deallocated(). Check setsockopt returns on both the lower and restore paths; preserve the first error via !err when the restore fails after a prior error so the actionable return code is not masked by a bookkeeping failure.
Cc: Günther Noack <[email protected]>
Cc: Thomas Weißschuh <[email protected]>
Cc: [email protected]
Depends-on: 07c2572a8757 ("selftests/landlock: Skip stale records in audit_match_record()")
--- tools/testing/selftests/landlock/audit.h | 80 ++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 17 deletions(-)
tools/testing/selftests/landlock/audit.h +63 -17
@@ -45,17 +45,25 @@ struct audit_message { }; }; -static const struct timeval audit_tv_dom_drop = { +static const struct timeval audit_tv_default = {/* -* Because domain deallocation is tied to asynchronous credential -* freeing, receiving such event may take some time. In practice, -* on a small VM, it should not exceed 100k usec, but let's wait up -* to 1 second to be safe. +* Default socket timeout for audit_match_record() callers that expect a +* record to arrive. Asynchronous kauditd delivery can exceed 1 usec +* under heavy debug configs (KASAN, lockdep), where kauditd_thread +* scheduling between audit_log_end() and netlink_unicast() takes longer +* than the previous 1 usec timeout. 1 second is a generous ceiling: on +* the happy path, kauditd delivers within dozens of usec. */ .tv_sec =1 , }; -static const struct timeval audit_tv_default = { +static const struct timeval audit_tv_fast = { +/* +* Fast timeout for paths that expect no record (audit_init() drain, +* audit_count_records(), probes). Causes audit_recv() to return +* -EAGAIN once the socket buffer is empty, naturally terminating the +* read loop. +*/ .tv_usec =1 , };
@@ -334,8 +342,13 @@ static int __maybe_unused matches_log_do* Matches a domain deallocation record. When expected_domain_id is non-zero, * the pattern includes the specific domain ID so that stale deallocation * records from a previous test (with a different domain ID) are skipped by -* audit_match_record(), and the socket timeout is temporarily increased to -* audit_tv_dom_drop to wait for the asynchronous kworker deallocation. +* audit_match_record(), waiting for the asynchronous kworker deallocation with +* the default patient timeout. +* +* When expected_domain_id is zero, the caller is probing for any dealloc record +* that may or may not arrive. Temporarily lowers the socket timeout to +* audit_tv_fast for this probe so it returns promptly when no record is +* pending; restores audit_tv_default after. */ static int __maybe_unused matches_log_domain_deallocated(int audit_fd, unsigned int num_denials,
@@ -361,16 +374,21 @@ matches_log_domain_deallocated(int auditif (log_match_len >=sizeof (log_match))return -E2BIG ; -if (expected_domain_id) -setsockopt (audit_fd,SOL_SOCKET ,SO_RCVTIMEO , - &audit_tv_dom_drop,sizeof (audit_tv_dom_drop)); +if (!expected_domain_id) { +if (setsockopt (audit_fd,SOL_SOCKET ,SO_RCVTIMEO , + &audit_tv_fast,sizeof (audit_tv_fast))) +return -errno; + } err =audit_match_record (audit_fd,AUDIT_LANDLOCK_DOMAIN , log_match, domain_id); -if (expected_domain_id) -setsockopt (audit_fd,SOL_SOCKET ,SO_RCVTIMEO , &audit_tv_default, -sizeof (audit_tv_default)); +if (!expected_domain_id) { +if (setsockopt (audit_fd,SOL_SOCKET ,SO_RCVTIMEO , + &audit_tv_default,sizeof (audit_tv_default)) && + !err) + err = -errno; + }return err; }
@@ -387,6 +405,11 @@ struct audit_records {* audit_init() and after the preceding audit_match_record() call. Allocation * records are emitted synchronously during landlock_log_denial() in the current * test's syscall context, so only those are counted in records->domain. +* +* Temporarily lowers SO_RCVTIMEO to audit_tv_fast for the read loop: this is a +* "no record expected" path that should terminate on the first -EAGAIN. The +* default patient timeout is restored on exit for subsequent +* audit_match_record() callers. */ static int audit_count_records (int audit_fd,struct audit_records *records) {
@@ -403,6 +426,12 @@ static int audit_count_records(int audit records->access =0 ; records->domain =0 ; +if (setsockopt (audit_fd,SOL_SOCKET ,SO_RCVTIMEO , &audit_tv_fast, +sizeof (audit_tv_fast))) { + err = -errno; + goto out; + } + do { memset(&msg, 0, sizeof(msg)); err = audit_recv(audit_fd, &msg);
@@ -429,6 +458,10 @@ static int audit_count_records(int audit }while (true); out: +if (setsockopt (audit_fd,SOL_SOCKET ,SO_RCVTIMEO , &audit_tv_default, +sizeof (audit_tv_default)) && + !err) + err = -errno;regfree (&dealloc_re);return err; }
@@ -449,9 +482,9 @@ static int audit_init(void)if (err) goto err_close; -/* Sets a timeout for negative tests. */ - err =setsockopt (fd,SOL_SOCKET ,SO_RCVTIMEO , &audit_tv_default, -sizeof (audit_tv_default)); +/* Uses the fast timeout to drain stale records below. */ + err =setsockopt (fd,SOL_SOCKET ,SO_RCVTIMEO , &audit_tv_fast, +sizeof (audit_tv_fast));if (err) { err = -errno; goto err_close;
@@ -467,6 +500,19 @@ static int audit_init(void)while (audit_recv (fd,NULL ) ==0 ) ; +/* +* Restores the default timeout for audit_match_record() callers that +* expect a record to arrive. Paths that expect no record restore the +* fast timeout locally (audit_count_records(), the expected_domain_id +* == 0 probe in matches_log_domain_deallocated()). +*/ + err =setsockopt (fd,SOL_SOCKET ,SO_RCVTIMEO , &audit_tv_default, +sizeof (audit_tv_default)); +if (err) { + err = -errno; + goto err_close; + } +return fd; err_close:
Greg Kroah-Hartman
maintainerPATCH 450/518 KVM: arm64: nv: Avoid dereferencing NULL VNCR pseudo-TLB
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Marc Zyngier <[email protected]>
commit 4be6cbeb93d26994bd1827ddbce391e3c4395c8f upstream.
VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and either can race against a vcpu not being onlined yet (no pseudo-TLB allocated). Similarly, the TLB might be invalid, and the invalidation should be skipped in this case.
Both kvm_invalidate_vncr_ipa() and kvm_invalidate_vncr_va() are expected to perform the same checks, except that the latter doesn't check for the allocation and blindly dereferences the pointer.
Solve this by introducing a new iterator built on top of the usual kvm_for_each_vcpu() that checks for both of the above conditions, and convert the two users to it.
--- arch/arm64/kvm/nested.c | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-)
arch/arm64/kvm/nested.c +15 -21
@@ -904,9 +904,21 @@ static void invalidate_vncr(struct vncr_clear_fixmap (vncr_fixmap (vt->cpu)); } +/* +* VNCR TLB invalidation occurs from MMU notifiers or TLBI instructions, and +* either can race against a vcpu not being onlined yet (no pseudo-TLB +* allocated). Similarly, the TLB might be invalid. Skip those, as they +* obviously don't participate in the invalidation at this stage. +*/ +#define kvm_for_each_vncr_tlb (idx, vcpup, tlbp, kvm) \ + kvm_for_each_vcpu(idx, vcpup, kvm) \ + if (((tlbp) = vcpup->arch.vncr_tlb) && \ + (tlbp)->valid) +static void kvm_invalidate_vncr_ipa (struct kvm *kvm,u64 start,u64 end) {struct kvm_vcpu *vcpu; +struct vncr_tlb *vt;unsigned long i;lockdep_assert_held_write (&kvm->mmu_lock);
@@ -914,24 +926,9 @@ static void kvm_invalidate_vncr_ipa(struif (!kvm_has_feat (kvm,ID_AA64MMFR4_EL1 , NV_frac,NV2_ONLY ))return ; -kvm_for_each_vcpu (i, vcpu, kvm) { -struct vncr_tlb *vt = vcpu->arch.vncr_tlb; +kvm_for_each_vncr_tlb (i, vcpu, vt, kvm) {u64 ipa_start, ipa_end, ipa_size; -/* -* Careful here: We end-up here from an MMU notifier, -* and this can race against a vcpu not being onlined -* yet, without the pseudo-TLB being allocated. -* -* Skip those, as they obviously don't participate in -* the invalidation at this stage. -*/ -if (!vt) -continue ; - -if (!vt->valid) -continue ; - ipa_size =ttl_to_size (pgshift_level_to_ttl (vt->wi.pgshift, vt->wr.level)); ipa_start = vt->wr.pa & ~(ipa_size -1 );
@@ -961,17 +958,14 @@ static void invalidate_vncr_va(struct kv struct s1e2_tlbi_scope *scope) {struct kvm_vcpu *vcpu; +struct vncr_tlb *vt;unsigned long i;lockdep_assert_held_write (&kvm->mmu_lock); -kvm_for_each_vcpu (i, vcpu, kvm) { -struct vncr_tlb *vt = vcpu->arch.vncr_tlb; +kvm_for_each_vncr_tlb (i, vcpu, vt, kvm) {u64 va_start, va_end, va_size; -if (!vt->valid) -continue ; - va_size =ttl_to_size (pgshift_level_to_ttl (vt->wi.pgshift, vt->wr.level)); va_start = vt->gva & ~(va_size -1 );
Greg Kroah-Hartman
maintainerPATCH 451/518 LoongArch: KVM: Add missing slots_lock for device register/unregister
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zeng Chi <[email protected]>
commit aeded601d6aceb57cdda4b2701d2ee00c43a8b69 upstream.
kvm_io_bus_register_dev() and kvm_io_bus_unregister_dev() should be called under kvm->slots_lock. The unregister calls in ipi.c, eiointc.c and pch_pic.c were also missing this protection. Add it to match the register side.
--- arch/loongarch/kvm/intc/eiointc.c | 6 ++++++ arch/loongarch/kvm/intc/ipi.c | 2 ++ arch/loongarch/kvm/intc/pch_pic.c | 2 ++ 3 files changed, 10 insertions(+)
arch/loongarch/kvm/intc/eiointc.c +6 -0
@@ -645,10 +645,14 @@ static int kvm_eiointc_create(struct kvm device = &s->device_vext;kvm_iodevice_init (device, &kvm_eiointc_virt_ops); +mutex_lock (&kvm->slots_lock); ret =kvm_io_bus_register_dev (kvm,KVM_IOCSR_BUS ,EIOINTC_VIRT_BASE ,EIOINTC_VIRT_SIZE , device); +mutex_unlock (&kvm->slots_lock);if (ret <0 ) { +mutex_lock (&kvm->slots_lock);kvm_io_bus_unregister_dev (kvm,KVM_IOCSR_BUS , &s->device); +mutex_unlock (&kvm->slots_lock);kfree (s);return ret; }
@@ -667,8 +671,10 @@ static void kvm_eiointc_destroy(struct k kvm = dev->kvm; eiointc = kvm->arch.eiointc; +mutex_lock (&kvm->slots_lock);kvm_io_bus_unregister_dev (kvm,KVM_IOCSR_BUS , &eiointc->device);kvm_io_bus_unregister_dev (kvm,KVM_IOCSR_BUS , &eiointc->device_vext); +mutex_unlock (&kvm->slots_lock);kfree (eiointc);kfree (dev); }
arch/loongarch/kvm/intc/ipi.c +2 -0
@@ -447,7 +447,9 @@ static void kvm_ipi_destroy(struct kvm_d kvm = dev->kvm; ipi = kvm->arch.ipi; +mutex_lock (&kvm->slots_lock);kvm_io_bus_unregister_dev (kvm,KVM_IOCSR_BUS , &ipi->device); +mutex_unlock (&kvm->slots_lock);kfree (ipi);kfree (dev); }
arch/loongarch/kvm/intc/pch_pic.c +2 -0
@@ -481,7 +481,9 @@ static void kvm_pch_pic_destroy(struct k kvm = dev->kvm; s = kvm->arch.pch_pic;/* unregister pch pic device and free it's memory */ +mutex_lock (&kvm->slots_lock);kvm_io_bus_unregister_dev (kvm,KVM_MMIO_BUS , &s->device); +mutex_unlock (&kvm->slots_lock);kfree (s);kfree (dev); }
Greg Kroah-Hartman
maintainerPATCH 452/518 KVM: arm64: Bound used_lrs when flushing the pKVM hyp vCPU
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
commit 8cc8bbbfab14c22c5551d0dd19b208a44b141c76 upstream.
flush_hyp_vcpu() copies the host vGIC state into the hyp's private vCPU on every run. The vGIC list register save and restore use used_lrs as their loop bound and expect it to stay within the number of implemented list registers. While this is generally the case, flush_hyp_vcpu() copies vgic_v3 verbatim and does not enforce this, so a value provided by the host is used at EL2 to index vgic_lr[] and access ICH_LR<n>_EL2 (host -> EL2).
Fix by clamping used_lrs to the number of implemented list registers after the copy, as the trusted path already does in vgic_flush_lr_state(). The number of implemented list registers is constant after init, so it is replicated once from kvm_vgic_global_state.nr_lr into hyp_gicv3_nr_lr rather than read on every entry.
--- arch/arm64/include/asm/kvm_hyp.h | 1 + arch/arm64/kvm/arm.c | 2 ++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 9 +++++++++ 3 files changed, 12 insertions(+)
arch/arm64/include/asm/kvm_hyp.h +1 -0
@@ -157,5 +157,6 @@ extern unsigned long kvm_nvhe_sym(__icacextern unsigned int kvm_nvhe_sym (kvm_arm_vmid_bits );extern unsigned int kvm_nvhe_sym (kvm_host_sve_max_vl );extern unsigned long kvm_nvhe_sym (hyp_nr_cpus ); +extern unsigned int kvm_nvhe_sym (hyp_gicv3_nr_lr );#endif /* __ARM64_KVM_HYP_H__ */
arch/arm64/kvm/arm.c +2 -0
@@ -2426,6 +2426,8 @@ static int __init init_subsystems(void)switch (err) {case 0 : vgic_present = true; +if (static_branch_unlikely (&kvm_vgic_global_state.gicv3_cpuif)) +kvm_nvhe_sym (hyp_gicv3_nr_lr) = kvm_vgic_global_state.nr_lr;break ;case -ENODEV :case -ENXIO :
arch/arm64/kvm/hyp/nvhe/hyp-main.c +9 -0
@@ -24,6 +24,9 @@ DEFINE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); +/* Number of implemented GICv3 LRs. Used by flush_hyp_vcpu(). */ +unsigned int hyp_gicv3_nr_lr; +void __kvm_hyp_host_forward_smc (struct kvm_cpu_context *host_ctxt);static void __hyp_sve_save_guest (struct kvm_vcpu *vcpu)
@@ -139,6 +142,12 @@ static void flush_hyp_vcpu(struct pkvm_h hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3 = host_vcpu->arch.vgic_cpu.vgic_v3; +/* Bound used_lrs by the number of implemented list registers. */ + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs = + min_t(unsigned int, + hyp_vcpu->vcpu.arch.vgic_cpu.vgic_v3.used_lrs, + hyp_gicv3_nr_lr); + hyp_vcpu->vcpu.arch.pid = host_vcpu->arch.pid; }
Greg Kroah-Hartman
maintainerPATCH 453/518 KVM: arm64: Clear __hyp_running_vcpu when flushing the pKVM hyp vCPU
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hyunwoo Kim <[email protected]>
commit e8042f6e1d7befb2fb6b10a75918642bcd0acf9a upstream.
flush_hyp_vcpu() copies the host vCPU context into the hyp's private vCPU on every run. ctxt_to_vcpu() expects a guest context to have a NULL __hyp_running_vcpu, which is only ever set on the host context, so that it resolves the vCPU via container_of(). While this is generally the case, flush_hyp_vcpu() copies the context verbatim and does not enforce this, so a value provided by the host is dereferenced at EL2 (host -> EL2).
Fix by clearing __hyp_running_vcpu after the copy.
--- arch/arm64/kvm/hyp/nvhe/hyp-main.c | 3 +++ 1 file changed, 3 insertions(+)
arch/arm64/kvm/hyp/nvhe/hyp-main.c +3 -0
@@ -131,6 +131,9 @@ static void flush_hyp_vcpu(struct pkvm_h hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt; +/* __hyp_running_vcpu must be NULL in a guest context. */ + hyp_vcpu->vcpu.arch.ctxt.__hyp_running_vcpu =NULL ; + hyp_vcpu->vcpu.arch.mdcr_el2 = host_vcpu->arch.mdcr_el2; hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI |HCR_TWE ); hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
Greg Kroah-Hartman
maintainerPATCH 454/518 KVM: SEV: Pin source page for write when adding CPUID data for SNP guest
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit f13e900599089b10113ceb36013423f0837c6792 upstream.
When populating a guest_memfd instance with the initial CPUID data for an SNP guest, acquire a writable pin on the source page as KVM will write back the "correct" CPUID information if the userspace provided data is rejected by trusted firmware. Because KVM writes to the source page using a kernel mapping, pinning for read could result in KVM clobbering read-only memory.
Note, well-behaved VMMs are unlikely to be affected, as CPUID information is almost always dynamically generated by userspace, i.e. it's unlikely for the CPUID information to be backed by a read-only mapping.
--- arch/x86/kvm/svm/sev.c | 1 + arch/x86/kvm/vmx/tdx.c | 2 +- include/linux/kvm_host.h | 3 ++- virt/kvm/guest_memfd.c | 6 ++++-- 4 files changed, 8 insertions(+), 4 deletions(-)
arch/x86/kvm/svm/sev.c +1 -0
@@ -2470,6 +2470,7 @@ static int snp_launch_update(struct kvm sev_populate_args.type = params.type; count =kvm_gmem_populate (kvm, params.gfn_start, src, npages, + params.type ==KVM_SEV_SNP_PAGE_TYPE_CPUID , sev_gmem_post_populate, &sev_populate_args);if (count <0 ) { argp->error = sev_populate_args.fw_error;
arch/x86/kvm/vmx/tdx.c +1 -1
@@ -3185,7 +3185,7 @@ static int tdx_vcpu_init_mem_region(stru }; gmem_ret =kvm_gmem_populate (kvm,gpa_to_gfn (region.gpa),u64_to_user_ptr (region.source_addr), -1 , tdx_gmem_post_populate, &arg); +1 , false, tdx_gmem_post_populate, &arg);if (gmem_ret <0 ) { ret = gmem_ret;break ;
include/linux/kvm_host.h +2 -1
@@ -2601,7 +2601,8 @@ int kvm_arch_gmem_prepare(struct kvm *kvtypedef int (*kvm_gmem_populate_cb )(struct kvm *kvm,gfn_t gfn,kvm_pfn_t pfn,struct page *page,void *opaque); -long kvm_gmem_populate (struct kvm *kvm,gfn_t gfn,void __user *src,long npages, +long kvm_gmem_populate (struct kvm *kvm,gfn_t start_gfn,void __user *src, +long npages,bool may_writeback_src,kvm_gmem_populate_cb post_populate,void *opaque);#endif
virt/kvm/guest_memfd.c +4 -2
@@ -858,7 +858,8 @@ out_unlock:return ret; } -long kvm_gmem_populate (struct kvm *kvm,gfn_t start_gfn,void __user *src,long npages, +long kvm_gmem_populate (struct kvm *kvm,gfn_t start_gfn,void __user *src, +long npages,bool may_writeback_src,kvm_gmem_populate_cb post_populate,void *opaque) {struct kvm_memory_slot *slot;
@@ -892,8 +893,9 @@ long kvm_gmem_populate(struct kvm *kvm,if (src) {unsigned long uaddr = (unsigned long )src + i *PAGE_SIZE ; +unsigned int flags = may_writeback_src ?FOLL_WRITE :0 ; - ret =get_user_pages_fast (uaddr,1 ,0 , &src_page); + ret =get_user_pages_fast (uaddr,1 , flags, &src_page);if (ret <0 )break ;if (ret !=1 ) {
Greg Kroah-Hartman
maintainerPATCH 455/518 KVM: SVM: Disable x2AVIC RDMSR interception for MSRs KVM actually supports
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit 7f4b7092d9a173a4271e28c0ed1fc235994e309b upstream.
When toggling x2AVIC on/off, use KVM's curated mask of x2APIC MSRs that can/should be passed through to the guest (or not) when 2AVIC is enabled. Using the effective list provided by the local APIC emulation fixes multiple (classes of) bugs, as the existing hand-coded list of MSRs is wrong on multiple fronts:
- ARBPRI isn't supported by KVM, isn't accelerated by AVIC (for read or write), and its #VMEXIT is fault-like, i.e. requires decoding the instruction. Disabling interception is nonsensical and suboptimal.
- DFR and ICR2 aren't supported by x2APIC and so don't need their intercepts disabled for performance reasons. While the #GP due to x2APIC being abled has higher priority than the trap-like #VMEXIT, disabling interception of unsupported MSRs is confusing and unnecessary.
- RRR is completely unsupported.
- AVIC currently fails to pass through the "range of vectors" registers, IRR, ISR, and TMR, as e.g. X2APIC_MSR(APIC_IRR) only affects IRR0, and thus only disables intercept for vectors 31:0 (which are the *least* interesting registers).
- TMCCT (the current APIC timer count) isn't accelerated by hardware, and generates a fault-like AVIC_UNACCELERATED_ACCESS #VMEXIT, i.e. requires KVM to decode the instruction to figure out what the guest was trying to access. Note, the only reason this isn't a fatal bug is that the AVIC architecture had the foresight to guard against buggy hypervisors. E.g. if hardware simply read from the virtual APIC page, the guest would get garbage (because the timer is emulated in software).
--- arch/x86/kvm/svm/avic.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
arch/x86/kvm/svm/avic.c +11 -2
@@ -122,6 +122,9 @@ static u32 x2avic_max_physical_id;static void avic_set_x2apic_msr_interception (struct vcpu_svm *svm,bool intercept) { +struct kvm_vcpu *vcpu = &svm->vcpu; +u64 rd_regs; +static const u32 x2avic_passthrough_msrs[] = {X2APIC_MSR (APIC_ID ),X2APIC_MSR (APIC_LVR ),
@@ -162,9 +165,15 @@ static void avic_set_x2apic_msr_intercepif (!x2avic_enabled)return ; + rd_regs =kvm_x2apic_disable_read_intercept_reg_mask (vcpu); + +for_each_set_bit (i, (unsigned long *)&rd_regs,BITS_PER_TYPE (rd_regs)) +svm_set_intercept_for_msr (vcpu,APIC_BASE_MSR + i, +MSR_TYPE_R , intercept); +for (i =0 ; i <ARRAY_SIZE (x2avic_passthrough_msrs); i++) -svm_set_intercept_for_msr (&svm->vcpu, x2avic_passthrough_msrs[i], -MSR_TYPE_RW , intercept); +svm_set_intercept_for_msr (vcpu, x2avic_passthrough_msrs[i], +MSR_TYPE_W , intercept); svm->x2avic_msrs_intercepted = intercept; }
Greg Kroah-Hartman
maintainerPATCH 456/518 KVM: SVM: Only disable x2AVIC WRMSR interception for MSRs that are accelerated
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit 8c63179d975f2029c948ecce622f72af616dbff7 upstream.
When x2AVIC is enabled, disable WRMSR interception only for MSRs that are actually accelerated by hardware. Disabling interception for MSRs that aren't accelerated is functionally "fine", and in some cases a weird "win" for performance, but only for cases that should never be triggered by a well-behaved VM (writes to read-only registers; the #GP will typically occur in the guest without taking a #VMEXIT, even for fault-like exits).
But overall, disabling interception for MSRs that aren't accelerated is at best confusing and unintuitive, and at worst introduces avoidable risk, as the APM's documentation is imperfect and contradictory. The table in "15.29.3.1 Virtual APIC Register Accesses" of simply states that such writes generate exits, where as "Section 15.29.10 x2AVIC" says:
x2APIC MSR intercept checks and access checks have higher priority than AVIC access permission checks.
CPU behavior follows the latter (which makes perfect sense), but all in all there's simply no reason to disable interception just to make a #GP faster.
Note, the set of MSRs that are passed through for write is identical to VMX's set when IPI virtualization is enabled. This is not a coincidence, and is another motiviating factor for cleaning up the intercepts, as x2AVIC is functionally equivalent to APICv+IPIv.
--- arch/x86/kvm/svm/avic.c | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-)
arch/x86/kvm/svm/avic.c +4 -36
@@ -124,39 +124,6 @@ static void avic_set_x2apic_msr_intercep {struct kvm_vcpu *vcpu = &svm->vcpu;u64 rd_regs; - -static const u32 x2avic_passthrough_msrs[] = { -X2APIC_MSR (APIC_ID ), -X2APIC_MSR (APIC_LVR ), -X2APIC_MSR (APIC_TASKPRI ), -X2APIC_MSR (APIC_ARBPRI ), -X2APIC_MSR (APIC_PROCPRI ), -X2APIC_MSR (APIC_EOI ), -X2APIC_MSR (APIC_RRR ), -X2APIC_MSR (APIC_LDR ), -X2APIC_MSR (APIC_DFR ), -X2APIC_MSR (APIC_SPIV ), -X2APIC_MSR (APIC_ISR ), -X2APIC_MSR (APIC_TMR ), -X2APIC_MSR (APIC_IRR ), -X2APIC_MSR (APIC_ESR ), -X2APIC_MSR (APIC_ICR ), -X2APIC_MSR (APIC_ICR2 ), - -/* -* Note! Always intercept LVTT, as TSC-deadline timer mode -* isn't virtualized by hardware, and the CPU will generate a -* #GP instead of a #VMEXIT. -*/ -X2APIC_MSR (APIC_LVTTHMR ), -X2APIC_MSR (APIC_LVTPC ), -X2APIC_MSR (APIC_LVT0 ), -X2APIC_MSR (APIC_LVT1 ), -X2APIC_MSR (APIC_LVTERR ), -X2APIC_MSR (APIC_TMICT ), -X2APIC_MSR (APIC_TMCCT ), -X2APIC_MSR (APIC_TDCR ), - };int i;if (intercept == svm->x2avic_msrs_intercepted)
@@ -171,9 +138,10 @@ static void avic_set_x2apic_msr_intercepsvm_set_intercept_for_msr (vcpu,APIC_BASE_MSR + i,MSR_TYPE_R , intercept); -for (i =0 ; i <ARRAY_SIZE (x2avic_passthrough_msrs); i++) -svm_set_intercept_for_msr (vcpu, x2avic_passthrough_msrs[i], -MSR_TYPE_W , intercept); +svm_set_intercept_for_msr (vcpu,X2APIC_MSR (APIC_TASKPRI ),MSR_TYPE_W , intercept); +svm_set_intercept_for_msr (vcpu,X2APIC_MSR (APIC_EOI ),MSR_TYPE_W , intercept); +svm_set_intercept_for_msr (vcpu,X2APIC_MSR (APIC_SELF_IPI ),MSR_TYPE_W , intercept); +svm_set_intercept_for_msr (vcpu,X2APIC_MSR (APIC_ICR ),MSR_TYPE_W , intercept); svm->x2avic_msrs_intercepted = intercept; }
Greg Kroah-Hartman
maintainerPATCH 457/518 KVM: VMX: Refresh GUEST_PENDING_DBG_EXCEPTIONS.BS on all injected #DBs
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit c5bad4fa2d5dfd8c25140051a9807eba387a19b8 upstream.
Move KVM's stuffing of GUEST_PENDING_DBG_EXCEPTIONS.BS when RFLAGS.TF=1 and MOV/POP SS or STI blocking is active into the exception injection code so that KVM fixes up the VMCS for all injected #DBs, not only those that are reflected back into the guest after #DB interception. E.g. if KVM queues a #DB in the emulator, or more importantly if userspace does save/restore exactly on the #DB+shadow boundary, then KVM needs to massage the VMCS to avoid the VM-Entry consistency check.
Opportunistically update the wording of the comment to describe the behavior as a workaround of flawed CPU behavior/architecture, to make it clear that the *only* thing KVM is doing is fudging around a consistency check. Per the SDM:
There are no pending debug exceptions after VM entry if any of the following are true:
* The VM entry is vectoring with one of the following interruption
types: external interrupt, non-maskable interrupt (NMI), hardware exception, or privileged software exception.
I.e. forcing GUEST_PENDING_DBG_EXCEPTIONS.BS does *not* impact guest- visible behavior.
--- arch/x86/kvm/vmx/vmx.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-)
arch/x86/kvm/vmx/vmx.c +18 -17
@@ -1908,6 +1908,24 @@ void vmx_inject_exception(struct kvm_vcpu32 intr_info = ex->vector |INTR_INFO_VALID_MASK ;struct vcpu_vmx *vmx =to_vmx (vcpu); +/* +* When injecting a #DB, single-stepping is enabled in RFLAGS, and STI +* or MOV-SS blocking is active, set vmcs.PENDING_DBG_EXCEPTIONS.BS to +* prevent a false positive from VM-Entry consistency check. VM-Entry +* asserts that a single-step #DB _must_ be pending in this scenario, +* as the previous instruction cannot have toggled RFLAGS.TF 0=>1 +* (because STI and POP/MOV don't modify RFLAGS), therefore the one +* instruction delay when activating single-step breakpoints must have +* already expired. However, the CPU isn't smart enough to peek at +* vmcs.VM_ENTRY_INTR_INFO_FIELD and so doesn't realize that yes, there +* is indeed a #DB pending/imminent. +*/ +if (ex->vector ==DB_VECTOR && + (vmx_get_rflags (vcpu) &X86_EFLAGS_TF ) && +vmx_get_interrupt_shadow (vcpu)) +vmcs_writel (GUEST_PENDING_DBG_EXCEPTIONS , +vmcs_readl (GUEST_PENDING_DBG_EXCEPTIONS ) |DR6_BS ); +kvm_deliver_exception_payload (vcpu, ex);if (ex->has_error_code) {
@@ -5477,26 +5495,9 @@ static int handle_exception_nmi(struct k* avoid single-step #DB and MTF updates, as ICEBP is * higher priority. Note, skipping ICEBP still clears * STI and MOVSS blocking. -* -* For all other #DBs, set vmcs.PENDING_DBG_EXCEPTIONS.BS -* if single-step is enabled in RFLAGS and STI or MOVSS -* blocking is active, as the CPU doesn't set the bit -* on VM-Exit due to #DB interception. VM-Entry has a -* consistency check that a single-step #DB is pending -* in this scenario as the previous instruction cannot -* have toggled RFLAGS.TF 0=>1 (because STI and POP/MOV -* don't modify RFLAGS), therefore the one instruction -* delay when activating single-step breakpoints must -* have already expired. Note, the CPU sets/clears BS -* as appropriate for all other VM-Exits types. */ if (is_icebp (intr_info))WARN_ON (!skip_emulated_instruction (vcpu)); -else if ((vmx_get_rflags (vcpu) &X86_EFLAGS_TF ) && - (vmcs_read32 (GUEST_INTERRUPTIBILITY_INFO ) & - (GUEST_INTR_STATE_STI |GUEST_INTR_STATE_MOV_SS ))) -vmcs_writel (GUEST_PENDING_DBG_EXCEPTIONS , -vmcs_readl (GUEST_PENDING_DBG_EXCEPTIONS ) |DR6_BS );kvm_queue_exception_p (vcpu,DB_VECTOR , dr6);return 1 ;
Greg Kroah-Hartman
maintainerPATCH 458/518 KVM: SEV: Dont terminate SNP VMs on #VMGEXIT without a registered GHCB
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit 1797e00bf802d64b859ea18505087daad92019c8 upstream.
If the guest attempts a non-MSR #VMGEXIT without the registered GHCB, return a GHCB_HV_RESP_MALFORMED_INPUT+GHCB_ERR_NOT_REGISTERED error to the guest instead of exiting KVM_RUN with -EINVAL (and in likelihood killing the VM). KVM has already mapped the requested GHCB, i.e. can cleanly report an error, and so exiting with -EINVAL is completely unjustified.
Message-ID: <[email protected]>
Message-ID: <[email protected]>
--- arch/x86/kvm/svm/sev.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
arch/x86/kvm/svm/sev.c +6 -3
@@ -4522,9 +4522,12 @@ int sev_handle_vmgexit(struct kvm_vcpu *sev_es_sync_from_ghcb (svm);/* SEV-SNP guest requires that the GHCB GPA must be registered */ -if (is_sev_snp_guest (vcpu) && !ghcb_gpa_is_registered (svm, ghcb_gpa)) { -vcpu_unimpl (&svm->vcpu,"vmgexit: GHCB GPA [%#llx] is not registered.\n" , ghcb_gpa); -return -EINVAL ; +if (is_sev_snp_guest (vcpu) && + !ghcb_gpa_is_registered (svm, control->ghcb_gpa)) { +vcpu_unimpl (vcpu,"vmgexit: GHCB GPA [%#llx] is not registered.\n" , + control->ghcb_gpa); +svm_vmgexit_bad_input (svm,GHCB_ERR_NOT_REGISTERED ); +return 1 ; } ret =sev_es_validate_vmgexit (svm);
Greg Kroah-Hartman
maintainerPATCH 459/518 KVM: VMX: Handle bad values on proxied writes to LBR MSRs
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xuanqing Shi <[email protected]>
commit ca674df13b195eb6d124ab059799d4e03fa40624 upstream.
Use the "safe" WRMSR API when writing LBRs on behalf of the guest (or host userspace), and propagate any errors back to the instigator, as the value being written is untrusted. E.g. if the guest (or host userspace) attempts to set reserved bits in LBR_SELECT, then KVM needs to return an error, and not WARN on the bad value.
Continue using the "unsafe" version of RDMSR, as it should be impossible to reach the helper with a completely bogus MSR, i.e. WARNing on RDMSR failure is very desirable, e.g. to make KVM bugs more visible.
unchecked MSR access error: WRMSR to 0x1c8 (tried to write 0x0000000000004000)
Call Trace: intel_pmu_set_msr+0x4e0/0x7f0 [kvm_intel] kvm_pmu_set_msr+0x17e/0x1c0 [kvm] kvm_set_msr_common+0xc76/0x1440 [kvm] vmx_set_msr+0x5e6/0x1570 [kvm_intel] kvm_emulate_wrmsr+0x54/0x1d0 [kvm] vmx_handle_exit+0x7fc/0x970 [kvm_intel]
--- arch/x86/kvm/vmx/pmu_intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
arch/x86/kvm/vmx/pmu_intel.c +4 -2
@@ -308,13 +308,15 @@ static bool intel_pmu_handle_lbr_msrs_ac*/ local_irq_disable ();if (lbr_desc->event->state ==PERF_EVENT_STATE_ACTIVE ) { +int err =0 ; +if (read)rdmsrq (index, msr_info->data);else -wrmsrq (index, msr_info->data); + err =wrmsrq_safe (index, msr_info->data);__set_bit (INTEL_PMC_IDX_FIXED_VLBR ,vcpu_to_pmu (vcpu)->pmc_in_use);local_irq_enable (); -return true; +return !err; }clear_bit (INTEL_PMC_IDX_FIXED_VLBR ,vcpu_to_pmu (vcpu)->pmc_in_use);local_irq_enable ();
Greg Kroah-Hartman
maintainerPATCH 460/518 KVM: TDX: Account all non-transient page allocations for per-TD structures
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit a8b2924676ec42d76597f849baba31acc3ba1bfc upstream.
Account all non-transient allocations associated with a single TD (or its vCPUs), as KVM's ABI is that allocations that are active for the lifetime of a VM are accounted. Leave temporary allocations, i.e. allocations that are freed within a single function/ioctl, unaccounted, to again align with KVM's existing behavior, e.g. see commit dd103407ca31 ("KVM: X86: Remove unnecessary GFP_KERNEL_ACCOUNT for temporary variables").
--- arch/x86/kvm/vmx/tdx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
arch/x86/kvm/vmx/tdx.c +7 -7
@@ -2387,20 +2387,20 @@ static int __tdx_td_init(struct kvm *kvm ret = -ENOMEM ; - tdr_page =alloc_page (GFP_KERNEL ); + tdr_page =alloc_page (GFP_KERNEL_ACCOUNT );if (!tdr_page) goto free_hkid; kvm_tdx->td.tdcs_nr_pages = tdx_sysinfo->td_ctrl.tdcs_base_size /PAGE_SIZE ;/* TDVPS = TDVPR(4K page) + TDCX(multiple 4K pages), -1 for TDVPR. */ kvm_tdx->td.tdcx_nr_pages = tdx_sysinfo->td_ctrl.tdvps_base_size /PAGE_SIZE -1 ; - tdcs_pages =kzalloc_objs (*kvm_tdx->td.tdcs_pages, - kvm_tdx->td.tdcs_nr_pages); + tdcs_pages =kzalloc_objs (*kvm_tdx->td.tdcs_pages, kvm_tdx->td.tdcs_nr_pages, +GFP_KERNEL_ACCOUNT );if (!tdcs_pages) goto free_tdr;for (i =0 ; i < kvm_tdx->td.tdcs_nr_pages; i++) { - tdcs_pages[i] =alloc_page (GFP_KERNEL ); + tdcs_pages[i] =alloc_page (GFP_KERNEL_ACCOUNT );if (!tdcs_pages[i]) goto free_tdcs; }
@@ -2875,7 +2875,7 @@ static int tdx_td_vcpu_init(struct kvm_vint ret, i;u64 err; - page =alloc_page (GFP_KERNEL ); + page =alloc_page (GFP_KERNEL_ACCOUNT );if (!page)return -ENOMEM ; tdx->vp.tdvpr_page = page;
@@ -2888,14 +2888,14 @@ static int tdx_td_vcpu_init(struct kvm_v tdx->vp.tdvpr_pa =page_to_phys (tdx->vp.tdvpr_page); tdx->vp.tdcx_pages =kcalloc (kvm_tdx->td.tdcx_nr_pages,sizeof (*tdx->vp.tdcx_pages), -GFP_KERNEL ); +GFP_KERNEL_ACCOUNT );if (!tdx->vp.tdcx_pages) { ret = -ENOMEM ; goto free_tdvpr; }for (i =0 ; i < kvm_tdx->td.tdcx_nr_pages; i++) { - page =alloc_page (GFP_KERNEL ); + page =alloc_page (GFP_KERNEL_ACCOUNT );if (!page) { ret = -ENOMEM ; goto free_tdcx;
Greg Kroah-Hartman
maintainerPATCH 461/518 KVM: x86: Ensure vendors exit handler runs before fastpath userspace exits
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit 0ffedf43910e44b76c2c1db4e9fbf12b268190c1 upstream.
Move the handling of fastpath userspace exits into vendor code to ensure KVM runs vendor specific operations that need to run before userspace gains control of the vCPU. E.g. for VMX (and soon to be for SVM as well), KVM needs to flush the PML buffer prior to exiting to userspace, otherwise any memory written by the final KVM_RUN might never be flagged as dirty.
Note, waiting to snapshot CR0 and CR3 until svm_handle_exit() is flawed in general, as that risks consuming stale state in a fastpath handler. That will be addressed in a future change.
--- arch/x86/kvm/svm/svm.c | 3 +++ arch/x86/kvm/vmx/vmx.c | 3 +++ arch/x86/kvm/x86.c | 3 --- 3 files changed, 6 insertions(+), 3 deletions(-)
arch/x86/kvm/svm/svm.c +3 -0
@@ -3663,6 +3663,9 @@ static int svm_handle_exit(struct kvm_vc vcpu->arch.cr3 = svm->vmcb->save.cr3; } +if (unlikely (exit_fastpath ==EXIT_FASTPATH_EXIT_USERSPACE )) +return 0 ; +if (is_guest_mode (vcpu)) {int vmexit;
arch/x86/kvm/vmx/vmx.c +3 -0
@@ -6698,6 +6698,9 @@ static int __vmx_handle_exit(struct kvm_if (enable_pml && !is_guest_mode (vcpu))vmx_flush_pml_buffer (vcpu); +if (unlikely (exit_fastpath ==EXIT_FASTPATH_EXIT_USERSPACE )) +return 0 ; +/* * KVM should never reach this point with a pending nested VM-Enter. * More specifically, short-circuiting VM-Entry to emulate L2 due to
arch/x86/kvm/x86.c +0 -3
@@ -11578,9 +11578,6 @@ static int vcpu_enter_guest(struct kvm_vif (vcpu->arch.apic_attention)kvm_lapic_sync_from_vapic (vcpu); -if (unlikely (exit_fastpath ==EXIT_FASTPATH_EXIT_USERSPACE )) -return 0 ; - r =kvm_x86_call (handle_exit)(vcpu, exit_fastpath);return r;
Greg Kroah-Hartman
maintainerPATCH 462/518 KVM: guest_memfd: Treat memslot binding offset+size as unsigned values
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit eba85fee7fc6cf28fec38a5bf3c378bef9a79ca6 upstream.
When binding a memslot to a guest_memfd file, treat the offset and size as unsigned values to fix a bug where the sum of the two can result in a false negative when checking for overflow against the size of the file. Passing unsigned values also avoids relying on somewhat obscure checks in other flows for safety, and tracks the offset and size as they are intended to be tracked, as unsigned values.
On 64-bit kernels, the number of pages a memslot contains and thus the size (and offset) of its guest_memfd binding are unsigned 64-bit values. Taking the offset+size as an loff_t instead of a uoff_t inadvertently converts the unsigned value to a signed value if the offset and/or size is massive.
Locally storing the offset and size as signed values is benign in and of itself (though even that is *extremely* difficult to discern), but operating on their sum is not.
For the offset, KVM explicitly checks against a negative value, which might seem like a bug as KVM could incorrectly reject a legitimate binding, but that's not actually the case as KVM_CREATE_GUEST_MEMFD takes a signed value for its size, i.e. a would-be-negative offset is also greater than the maximum possible size of any guest_memfd file.
Regarding the size, while KVM lacks an explicit check for a negative value, i.e. seemingly has a flawed overflow check, KVM restricts the number of pages in a single memslot to the largest positive signed 32-bit value:
if (id < KVM_USER_MEM_SLOTS &&
(mem->memory_size >> PAGE_SHIFT) > KVM_MEM_MAX_NR_PAGES)
return -EINVAL;and so that maximum "size" will ever be is 0x7fffffff000.
The sum of the two is, however, problematic. While the size is restricted by KVM's memslot logic, the offset is not, i.e. the offset is completely unchecked until the "offset + size > i_size_read(inode)" check. If the offset is the (nearly) largest possible _positive_ value, then adding size to the offset can result in a signed, negative 64-bit value. When compared against the size of the file (guaranteed to be positive), the negative sum is always smaller, and KVM incorrectly allows the absurd offset.
Opportunistically add missing includes in kvm_mm.h (instead of relying on its parents).
--- virt/kvm/guest_memfd.c | 8 ++++---- virt/kvm/kvm_mm.h | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-)
virt/kvm/guest_memfd.c +4 -4
@@ -640,15 +640,16 @@ int kvm_gmem_create(struct kvm *kvm, str }int kvm_gmem_bind (struct kvm *kvm,struct kvm_memory_slot *slot, -unsigned int fd,loff_t offset) +unsigned int fd,uoff_t offset) { -loff_t size = slot->npages <<PAGE_SHIFT ; +uoff_t size = slot->npages <<PAGE_SHIFT ;unsigned long start, end;struct gmem_file *f;struct inode *inode;struct file *file;int r = -EINVAL ; +BUILD_BUG_ON (sizeof (gpa_t) !=sizeof (offset));BUILD_BUG_ON (sizeof (gfn_t) !=sizeof (slot->gmem.pgoff)); file =fget (fd);
@@ -664,8 +665,7 @@ int kvm_gmem_bind(struct kvm *kvm, struc inode =file_inode (file); -if (offset <0 || !PAGE_ALIGNED (offset) || - offset + size >i_size_read (inode)) +if (!PAGE_ALIGNED (offset) || offset + size >i_size_read (inode)) goto err;filemap_invalidate_lock (inode->i_mapping);
virt/kvm/kvm_mm.h +5 -2
@@ -3,6 +3,9 @@#ifndef __KVM_MM_H__#define __KVM_MM_H__ 1 +#include <linux/kvm.h> +#include <linux/kvm_types.h> +/* * Architectures can choose whether to use an rwlock or spinlock * for the mmu_lock. These macros, for use in common code
@@ -72,7 +75,7 @@ int kvm_gmem_init(struct module *module)void kvm_gmem_exit (void );int kvm_gmem_create (struct kvm *kvm,struct kvm_create_guest_memfd *args);int kvm_gmem_bind (struct kvm *kvm,struct kvm_memory_slot *slot, -unsigned int fd,loff_t offset); +unsigned int fd,uoff_t offset);void kvm_gmem_unbind (struct kvm_memory_slot *slot);#else static inline int kvm_gmem_init (struct module *module)
@@ -82,7 +85,7 @@ static inline int kvm_gmem_init(struct mstatic inline void kvm_gmem_exit (void ) {};static inline int kvm_gmem_bind (struct kvm *kvm,struct kvm_memory_slot *slot, -unsigned int fd,loff_t offset) +unsigned int fd,uoff_t offset) {WARN_ON_ONCE (1 );return -EIO ;
Greg Kroah-Hartman
maintainerPATCH 463/518 KVM: x86: Add dedicated API for getting mask of accelerated x2APIC MSRs
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sean Christopherson <[email protected]>
commit 8ba621f335a519b47cb7d3e3f4f15b5101b3a56f upstream.
Add a dedicated local APIC API, kvm_x2apic_disable_intercept_reg_mask(), to provide the mask of x2APIC registers whose MSRs can and should be passed through to the guest when x2APIC virtualization is enable, and use it in lieu of the open-coded equivalent VMX logic. Providing a common helper will allow sharing the logic with SVM (x2AVIC), and as a bonus eliminates the somewhat confusing code where KVM enables interception for MSR_TYPE_RW, even though only the READ case actually needs to be updated.
No functional change intended.
--- arch/x86/kvm/lapic.c | 21 +++++++++++++++++++-- arch/x86/kvm/lapic.h | 2 +- arch/x86/kvm/vmx/vmx.c | 3 +-- 3 files changed, 21 insertions(+), 5 deletions(-)
arch/x86/kvm/lapic.c +19 -2
@@ -1732,7 +1732,7 @@ static inline struct kvm_lapic *to_lapic#define APIC_REGS_MASK (first, count) \ (APIC_REG_MASK(first) * ((1ull << (count)) - 1)) -u64 kvm_lapic_readable_reg_mask (struct kvm_lapic *apic) +static u64 kvm_lapic_readable_reg_mask (struct kvm_lapic *apic) {/* Leave bits '0' for reserved and write-only registers. */ u64 valid_reg_mask =
@@ -1768,7 +1768,24 @@ u64 kvm_lapic_readable_reg_mask(struct kreturn valid_reg_mask; } -EXPORT_SYMBOL_FOR_KVM_INTERNAL (kvm_lapic_readable_reg_mask); + +u64 kvm_x2apic_disable_read_intercept_reg_mask (struct kvm_vcpu *vcpu) +{ +if (WARN_ON_ONCE (!lapic_in_kernel (vcpu))) +return 0 ; + +/* +* TMMCT, a.k.a. the current APIC timer count, reads aren't accelerated +* by hardware (Intel or AMD) as the timer is emulated in software (by +* KVM), i.e. reads from the virtual APIC page would return garbage. +* Intercept RDMSR, as handling the fault-like APIC-access VM-Exit is +* more expensive than handling a RDMSR VM-Exit (the APIC-access exit +* requires slow emulation of the code stream). +*/ +return kvm_lapic_readable_reg_mask (vcpu->arch.apic) & + ~APIC_REG_MASK (APIC_TMCCT ); +} +EXPORT_SYMBOL_FOR_KVM_INTERNAL (kvm_x2apic_disable_read_intercept_reg_mask);static int kvm_lapic_reg_read (struct kvm_lapic *apic,u32 offset,int len,void *data)
arch/x86/kvm/lapic.h +1 -1
@@ -157,7 +157,7 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpint kvm_lapic_set_pv_eoi (struct kvm_vcpu *vcpu,u64 data,unsigned long len);void kvm_lapic_exit (void ); -u64 kvm_lapic_readable_reg_mask (struct kvm_lapic *apic); +u64 kvm_x2apic_disable_read_intercept_reg_mask (struct kvm_vcpu *vcpu);static inline void kvm_lapic_set_irr (int vec,struct kvm_lapic *apic) {
arch/x86/kvm/vmx/vmx.c +1 -2
@@ -4159,7 +4159,7 @@ static void vmx_update_msr_bitmap_x2apic* mode, only the current timer count needs on-demand emulation by KVM. */ if (mode &MSR_BITMAP_MODE_X2APIC_APICV ) - msr_bitmap[read_idx] = ~kvm_lapic_readable_reg_mask (vcpu->arch.apic); + msr_bitmap[read_idx] = ~kvm_x2apic_disable_read_intercept_reg_mask (vcpu);else msr_bitmap[read_idx] = ~0ull ; msr_bitmap[write_idx] = ~0ull ;
@@ -4172,7 +4172,6 @@ static void vmx_update_msr_bitmap_x2apic !(mode & MSR_BITMAP_MODE_X2APIC));if (mode &MSR_BITMAP_MODE_X2APIC_APICV ) { -vmx_enable_intercept_for_msr (vcpu,X2APIC_MSR (APIC_TMCCT ),MSR_TYPE_RW );vmx_disable_intercept_for_msr (vcpu,X2APIC_MSR (APIC_EOI ),MSR_TYPE_W );vmx_disable_intercept_for_msr (vcpu,X2APIC_MSR (APIC_SELF_IPI ),MSR_TYPE_W );if (enable_ipiv)
Greg Kroah-Hartman
maintainerPATCH 464/518 KVM: arm64: Dont leak PFN when kvm_translate_vncr() races MMU notifier
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Oliver Upton <[email protected]>
commit 9f76b039a72d7e06374aa96862f0232ed53f7787 upstream.
In the case that kvm_translate_vncr() races with an MMU notifier the early return does not release a reference on the faulted in PFN. Add the necessary call to kvm_release_faultin_page() for the unused PFN.
--- arch/arm64/kvm/nested.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/arm64/kvm/nested.c +3 -1
@@ -1327,8 +1327,10 @@ static int kvm_translate_vncr(struct kvm } scoped_guard(write_lock, &vcpu->kvm->mmu_lock) { -if (mmu_invalidate_retry (vcpu->kvm, mmu_seq)) +if (mmu_invalidate_retry (vcpu->kvm, mmu_seq)) { +kvm_release_faultin_page (vcpu->kvm, page, true, false);return -EAGAIN ; + } vt->gva = va; vt->hpa = pfn <<PAGE_SHIFT ;
Greg Kroah-Hartman
maintainerPATCH 465/518 udmabuf: fix DMA direction mismatch in release_udmabuf()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Gavrilov <[email protected]>
commit fb7b1a0ab25a6077d26cb3829e31743972d4f31d upstream.
begin_cpu_udmabuf() maps the sg_table with the caller-provided direction (e.g., DMA_TO_DEVICE for a write-only sync), and caches it in ubuf->sg for reuse. However, release_udmabuf() always unmaps this sg_table with a hardcoded DMA_BIDIRECTIONAL, regardless of the direction that was originally used for the mapping.
With CONFIG_DMA_API_DEBUG=y this produces:
DMA-API: misc udmabuf: device driver frees DMA memory with different direction [device address=0x000000044a123000] [size=4096 bytes] [mapped with DMA_TO_DEVICE] [unmapped with DMA_BIDIRECTIONAL]
The issue was found during video playback when GStreamer performed a write-only DMA_BUF_IOCTL_SYNC on a udmabuf. It can be reproduced with CONFIG_DMA_API_DEBUG=y by creating a udmabuf from a memfd, performing a write-only sync (DMA_BUF_SYNC_WRITE without DMA_BUF_SYNC_READ), and closing the file descriptor.
Fix this by storing the DMA direction used when the sg_table is first created in begin_cpu_udmabuf(), and passing that same direction to put_sg_table() in release_udmabuf().
--- drivers/dma-buf/udmabuf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/dma-buf/udmabuf.c +4 -1
@@ -40,6 +40,7 @@ struct udmabuf {struct folio **pinned_folios;struct sg_table *sg; +enum dma_data_direction sg_dir;struct miscdevice *device;pgoff_t *offsets; };
@@ -235,7 +236,7 @@ static void release_udmabuf(struct dma_bstruct device *dev = ubuf->device->this_device;if (ubuf->sg) -put_sg_table (dev, ubuf->sg,DMA_BIDIRECTIONAL ); +put_sg_table (dev, ubuf->sg, ubuf->sg_dir);deinit_udmabuf (ubuf);kfree (ubuf);
@@ -253,6 +254,8 @@ static int begin_cpu_udmabuf(struct dma_if (IS_ERR (ubuf->sg)) { ret =PTR_ERR (ubuf->sg); ubuf->sg =NULL ; + }else { + ubuf->sg_dir = direction; } }else {dma_sync_sgtable_for_cpu (dev, ubuf->sg, direction);
Greg Kroah-Hartman
maintainerPATCH 466/518 dma-buf/udmabuf: skip redundant cpu sync to fix cacheline EEXIST warning
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Gavrilov <[email protected]>
commit 504e2b4ab97a51d56d966cd36d0997ad30b65b2d upstream.
When CONFIG_DMA_API_DEBUG_SG is enabled, importing a udmabuf into a DRM driver (e.g. amdgpu for video playback in GNOME Videos / Showtime) triggers a spurious warning:
DMA-API: amdgpu 0000:03:00.0: cacheline tracking EEXIST, \
overlapping mappings aren't supported
WARNING: kernel/dma/debug.c:619 at add_dma_entry+0x473/0x5f0The call chain is:
amdgpu_cs_ioctl
-> amdgpu_ttm_backend_bind
-> dma_buf_map_attachment
-> [udmabuf] map_udmabuf -> get_sg_table
-> dma_map_sgtable(dev, sg, direction, 0) // attrs=0
-> debug_dma_map_sg -> add_dma_entry -> EEXISTThis happens because udmabuf builds a per-page scatter-gather list via sg_set_folio(). When begin_cpu_udmabuf() has already created an sg table mapped for the misc device, and an importer such as amdgpu maps the same pages for its own device via map_udmabuf(), the DMA debug infrastructure sees two active mappings whose physical addresses share cacheline boundaries and warns about the overlap.
The DMA_ATTR_SKIP_CPU_SYNC flag suppresses this check in add_dma_entry() because it signals that no CPU cache maintenance is performed at map/unmap time, making the cacheline overlap harmless.
All other major dma-buf exporters already pass this flag:
- drm_gem_map_dma_buf() passes DMA_ATTR_SKIP_CPU_SYNC
- amdgpu_dma_buf_map() passes DMA_ATTR_SKIP_CPU_SYNC
The CPU sync at map/unmap time is also redundant for udmabuf:begin_cpu_udmabuf() and end_cpu_udmabuf() already perform explicit cache synchronization via dma_sync_sgtable_for_cpu/device() when CPU access is requested through the dma-buf interface.
Pass DMA_ATTR_SKIP_CPU_SYNC to dma_map_sgtable() and dma_unmap_sgtable() in udmabuf to suppress the spurious warning and skip the redundant sync.
--- drivers/dma-buf/udmabuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/dma-buf/udmabuf.c +2 -2
@@ -163,7 +163,7 @@ static struct sg_table *get_sg_table(strsg_set_folio (sgl, ubuf->folios[i],PAGE_SIZE , ubuf->offsets[i]); - ret =dma_map_sgtable (dev, sg, direction,0 ); + ret =dma_map_sgtable (dev, sg, direction,DMA_ATTR_SKIP_CPU_SYNC );if (ret <0 ) goto err_map;return sg;
@@ -178,7 +178,7 @@ err_alloc:static void put_sg_table (struct device *dev,struct sg_table *sg,enum dma_data_direction direction) { -dma_unmap_sgtable (dev, sg, direction,0 ); +dma_unmap_sgtable (dev, sg, direction,DMA_ATTR_SKIP_CPU_SYNC );sg_free_table (sg);kfree (sg); }
Greg Kroah-Hartman
maintainerPATCH 467/518 svcrdma: wake sq waiters when the transport closes
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit e5248a7426030db1e126363f72afdb3b71339a5c upstream.
Threads parked in svc_rdma_sq_wait() on sc_sq_ticket_wait or sc_send_wait can hang indefinitely in TASK_UNINTERRUPTIBLE state across transport teardown, pinning svc_xprt references and blocking svc_rdma_free().
The close path sets XPT_CLOSE before invoking xpo_detach and both wait_event predicates include an XPT_CLOSE term, but the predicates are re-evaluated only on wakeup. sc_sq_ticket_wait has no completion-driven wake path; it is advanced solely by the chained ticket handoff inside svc_rdma_sq_wait() itself. Without an explicit wake at close, parked threads never observe XPT_CLOSE, hold their svc_xprt_get reference forever, and svc_rdma_free() blocks on xpt_ref dropping to zero.
Two close entry points reach this transport. Local teardown runs svc_rdma_detach() from svc_handle_xprt() -> svc_delete_xprt() -> xpo_detach() on a worker thread. A remote disconnect arrives at svc_rdma_cma_handler(), which calls svc_xprt_deferred_close():
that sets XPT_CLOSE and enqueues the transport but does not access either RDMA waitqueue, so a worker already parked in svc_rdma_sq_wait() never re-evaluates its predicate. With every worker parked on this transport, no thread is available to run the local teardown either, and the wake site there is unreachable.
Introduce svc_rdma_xprt_deferred_close(), a thin svcrdma wrapper that calls svc_xprt_deferred_close() and then wakes both sc_sq_ticket_wait and sc_send_wait. Convert the svcrdma producers that called svc_xprt_deferred_close() directly:svc_rdma_cma_handler(), qp_event_handler(), svc_rdma_post_send_err(), svc_rdma_wc_send(), the sendto drop path, the rw completion error paths, and the recvfrom flush and read-list error paths.
Wake both waitqueues from svc_rdma_detach() as well. The synchronous svc_xprt_close() path (backchannel ENOTCONN, device removal via svc_rdma_xprt_done) reaches detach without flowing through svc_xprt_deferred_close() and therefore does not invoke the new helper.
--- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 4 ++-- net/sunrpc/xprtrdma/svc_rdma_rw.c | 6 +++--- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 6 +++--- net/sunrpc/xprtrdma/svc_rdma_transport.c | 30 ++++++++++++++++++++++++++++-- 5 files changed, 37 insertions(+), 10 deletions(-)
include/linux/sunrpc/svc_rdma.h +1 -0
@@ -328,6 +328,7 @@ extern int svc_rdma_result_payload(struc unsigned int length);/* svc_rdma_transport.c */ +extern void svc_rdma_xprt_deferred_close (struct svcxprt_rdma *rdma);extern struct svc_xprt_class svc_rdma_class;#ifdef CONFIG_SUNRPC_BACKCHANNEL extern struct svc_xprt_class svc_rdma_bc_class;
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +2 -2
@@ -377,7 +377,7 @@ flushed:trace_svcrdma_wc_recv_err (wc, &ctxt->rc_cid); dropped:svc_rdma_recv_ctxt_put (rdma, ctxt); -svc_xprt_deferred_close (&rdma->sc_xprt); +svc_rdma_xprt_deferred_close (rdma); }/**
@@ -1001,7 +1001,7 @@ out_readlist:if (ret == -EINVAL )svc_rdma_send_error (rdma_xprt, ctxt, ret);svc_rdma_recv_ctxt_put (rdma_xprt, ctxt); -svc_xprt_deferred_close (xprt); +svc_rdma_xprt_deferred_close (rdma_xprt);return ret; }return 0 ;
net/sunrpc/xprtrdma/svc_rdma_rw.c +3 -3
@@ -313,7 +313,7 @@ static void svc_rdma_reply_done(struct itrace_svcrdma_wc_reply_err (wc, &cc->cc_cid); } -svc_xprt_deferred_close (&rdma->sc_xprt); +svc_rdma_xprt_deferred_close (rdma); }/**
@@ -345,7 +345,7 @@ static void svc_rdma_write_done(struct i* some of the outgoing RPC message. Signal the loss * to the client by closing the connection. */ -svc_xprt_deferred_close (&rdma->sc_xprt); +svc_rdma_xprt_deferred_close (rdma); }/**
@@ -390,7 +390,7 @@ static void svc_rdma_wc_read_done(struct*/ svc_rdma_cc_release (rdma, cc,DMA_FROM_DEVICE );svc_rdma_recv_ctxt_put (rdma, ctxt); -svc_xprt_deferred_close (&rdma->sc_xprt); +svc_rdma_xprt_deferred_close (rdma); }/*
net/sunrpc/xprtrdma/svc_rdma_sendto.c +3 -3
@@ -395,7 +395,7 @@ int svc_rdma_post_send_err(struct svcxpr int sqecount, int ret) {trace_svcrdma_sq_post_err (rdma ,cid ,ret ); -svc_xprt_deferred_close (&rdma->sc_xprt); +svc_rdma_xprt_deferred_close (rdma);/* If even one WR was posted, a Send completion will * return the reserved SQ slots.
@@ -437,7 +437,7 @@ flushed:else trace_svcrdma_wc_send_flush (wc, &ctxt->sc_cid);svc_rdma_send_ctxt_put (rdma, ctxt); -svc_xprt_deferred_close (&rdma->sc_xprt); +svc_rdma_xprt_deferred_close (rdma); }/**
@@ -1158,7 +1158,7 @@ put_ctxt:svc_rdma_send_ctxt_put (rdma, sctxt); drop_connection:trace_svcrdma_send_err (rqstp, ret); -svc_xprt_deferred_close (&rdma->sc_xprt); +svc_rdma_xprt_deferred_close (rdma);return -ENOTCONN ; }
net/sunrpc/xprtrdma/svc_rdma_transport.c +28 -2
@@ -98,10 +98,27 @@ struct svc_xprt_class svc_rdma_class = { .xcl_ident = XPRT_TRANSPORT_RDMA, }; +/** +* svc_rdma_xprt_deferred_close - Close an RDMA transport (deferred) +* @rdma: transport to close +*/ +void svc_rdma_xprt_deferred_close (struct svcxprt_rdma *rdma) +{ +svc_xprt_deferred_close (&rdma->sc_xprt); + +/* Release parked sc_sq_ticket_wait and sc_send_wait waiters. +* Once XPT_CLOSE is observed each returns -ENOTCONN. +*/ +wake_up_all (&rdma->sc_sq_ticket_wait); +wake_up_all (&rdma->sc_send_wait); +} +/* QP event handler */ static void qp_event_handler (struct ib_event *event,void *context) {struct svc_xprt *xprt = context; +struct svcxprt_rdma *rdma = + container_of(xprt, struct svcxprt_rdma, sc_xprt);trace_svcrdma_qp_error (event, (struct sockaddr *)&xprt->xpt_remote);switch (event->event) {
@@ -119,7 +136,7 @@ static void qp_event_handler(struct ib_ecase IB_EVENT_QP_ACCESS_ERR :case IB_EVENT_DEVICE_FATAL :default : -svc_xprt_deferred_close (xprt); +svc_rdma_xprt_deferred_close (rdma);break ; } }
@@ -340,7 +357,7 @@ static int svc_rdma_cma_handler(struct rsvc_xprt_enqueue (xprt);break ;case RDMA_CM_EVENT_DISCONNECTED : -svc_xprt_deferred_close (xprt); +svc_rdma_xprt_deferred_close (rdma);break ;default :break ;
@@ -597,6 +614,15 @@ static void svc_rdma_detach(struct svc_x container_of(xprt, struct svcxprt_rdma, sc_xprt);rdma_disconnect (rdma->sc_cm_id); + +/* +* Most close paths go through svc_rdma_xprt_deferred_close(), +* which wakes the SQ waitqueues. svc_xprt_close() reaches +* detach without that helper, so wake any threads parked in +* svc_rdma_sq_wait() here as well. +*/ +wake_up_all (&rdma->sc_sq_ticket_wait); +wake_up_all (&rdma->sc_send_wait); }/**
Greg Kroah-Hartman
maintainerPATCH 468/518 Revert "svcrdma: Use contiguous pages for RDMA Read sink buffers"
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chuck Lever <[email protected]>
commit a39f0ce0c9da20986b429e2db3e4e8739035d61b upstream.
Jonathan Flynn reports that commit 18755b8c2f24 ("svcrdma: Use contiguous pages for RDMA Read sink buffers") regresses NFS/RDMA WRITE throughput from 73.9 GiB/s to 30.3 GiB/s on a 128-core single-NUMA-node server driving dual 400Gb/s links with 640 nfsd threads. Server CPU utilization rises from 8.5% to 76%, with roughly three quarters of all cycles spent spinning on zone->lock.
The sink buffers are allocated as high-order page blocks, split into single pages so each sub-page carries an independent refcount, and later released one page at a time through folio batches. The per-CPU page caches cannot satisfy an allocation stream whose alloc order differs from its free order, so every sink buffer page makes a round trip through the buddy allocator's free lists, serialized on the zone lock of the single NUMA node. The rq_pages entries that the split pages displace, bulk-allocated moments earlier by svc_alloc_arg(), are freed without ever being used, doubling the allocator traffic.
The regression cannot be addressed trivially. Revert the commit now; a reworked approach can return in an upcoming merge window.
--- net/sunrpc/xprtrdma/svc_rdma_rw.c | 223 -------------------------------------- 1 file changed, 223 deletions(-)
net/sunrpc/xprtrdma/svc_rdma_rw.c +0 -223
@@ -754,216 +754,6 @@ int svc_rdma_prepare_reply_chunk(structreturn xdr->len; } -/* -* Cap contiguous RDMA Read sink allocations at order-4. -* Higher orders risk allocation failure under -* __GFP_NORETRY, which would negate the benefit of the -* contiguous fast path. -*/ -#define SVC_RDMA_CONTIG_MAX_ORDER 4 - -/** -* svc_rdma_alloc_read_pages - Allocate physically contiguous pages -* @nr_pages: number of pages needed -* @order: on success, set to the allocation order -* -* Attempts a higher-order allocation, falling back to smaller orders. -* The returned pages are split immediately so each sub-page has its -* own refcount and can be freed independently. -* -* Returns a pointer to the first page on success, or NULL if even -* order-1 allocation fails. -*/ -static struct page * -svc_rdma_alloc_read_pages (unsigned int nr_pages,unsigned int *order) -{ -unsigned int o; -struct page *page; - - o =min (get_order (nr_pages <<PAGE_SHIFT ), -SVC_RDMA_CONTIG_MAX_ORDER ); - -while (o >=1 ) { - page =alloc_pages (GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN, - o); -if (page) { -split_page (page, o); - *order = o; -return page; - } - o--; - } -return NULL ; -} - -/* -* svc_rdma_fill_contig_bvec - Replace rq_pages with a contiguous allocation -* @rqstp: RPC transaction context -* @head: context for ongoing I/O -* @bv: bvec entry to fill -* @pages_left: number of data pages remaining in the segment -* @len_left: bytes remaining in the segment -* -* On success, fills @bv with a bvec spanning the contiguous range and -* advances rc_curpage/rc_page_count. Returns the byte length covered, -* or zero if the allocation failed or would overrun rq_maxpages. -*/ -static unsigned int -svc_rdma_fill_contig_bvec (struct svc_rqst *rqstp, -struct svc_rdma_recv_ctxt *head, -struct bio_vec *bv,unsigned int pages_left, -unsigned int len_left) -{ -unsigned int order, npages, chunk_pages, chunk_len, i; -struct page *page; - - page =svc_rdma_alloc_read_pages (pages_left, &order); -if (!page) -return 0 ; - npages =1 << order; - -if (head->rc_curpage + npages > rqstp->rq_maxpages) { -for (i =0 ; i < npages; i++) -__free_page (page + i); -return 0 ; - } - -/* -* Replace rq_pages[] entries with pages from the contiguous -* allocation. If npages exceeds chunk_pages, the extra pages -* stay in rq_pages[] for later reuse or normal rqst teardown. -*/ -for (i =0 ; i < npages; i++) { -svc_rqst_page_release (rqstp, - rqstp->rq_pages[head->rc_curpage + i]); - rqstp->rq_pages[head->rc_curpage + i] = page + i; - } - - chunk_pages =min (npages, pages_left); - chunk_len = min_t(unsigned int, chunk_pages << PAGE_SHIFT, len_left); -bvec_set_page (bv, page, chunk_len,0 ); - head->rc_page_count += chunk_pages; - head->rc_curpage += chunk_pages; -return chunk_len; -} - -/* -* svc_rdma_fill_page_bvec - Add a single rq_page to the bvec array -* @head: context for ongoing I/O -* @ctxt: R/W context whose bvec array is being filled -* @cur: page to add -* @bvec_idx: pointer to current bvec index, not advanced on merge -* @len_left: bytes remaining in the segment -* -* If @cur is physically contiguous with the preceding bvec, it is -* merged by extending that bvec's length. Otherwise a new bvec -* entry is created. Returns the byte length covered. -*/ -static unsigned int -svc_rdma_fill_page_bvec (struct svc_rdma_recv_ctxt *head, -struct svc_rdma_rw_ctxt *ctxt,struct page *cur, -unsigned int *bvec_idx,unsigned int len_left) -{ - unsigned int chunk_len = min_t(unsigned int, PAGE_SIZE, len_left); - - head->rc_page_count++; - head->rc_curpage++; - -if (*bvec_idx >0 ) { -struct bio_vec *prev = &ctxt->rw_bvec[*bvec_idx -1 ]; - -if (page_to_phys (prev->bv_page) + prev->bv_offset + - prev->bv_len ==page_to_phys (cur)) { - prev->bv_len += chunk_len; -return chunk_len; - } - } - -bvec_set_page (&ctxt->rw_bvec[*bvec_idx], cur, chunk_len,0 ); - (*bvec_idx)++; -return chunk_len; -} - -/** -* svc_rdma_build_read_segment_contig - Build RDMA Read WR with contiguous pages -* @rqstp: RPC transaction context -* @head: context for ongoing I/O -* @segment: co-ordinates of remote memory to be read -* -* Greedily allocates higher-order pages to cover the segment, -* building one bvec per contiguous chunk. Each allocation is -* split so sub-pages have independent refcounts. When a -* higher-order allocation fails, remaining pages are covered -* individually, merging adjacent pages into the preceding bvec -* when they are physically contiguous. The split sub-pages -* replace entries in rq_pages[] so downstream cleanup is -* unchanged. -* -* Returns: -* %0: the Read WR was constructed successfully -* %-ENOMEM: allocation failed -* %-EIO: a DMA mapping error occurred -*/ -static int svc_rdma_build_read_segment_contig (struct svc_rqst *rqstp, -struct svc_rdma_recv_ctxt *head, -const struct svc_rdma_segment *segment) -{ -struct svcxprt_rdma *rdma =svc_rdma_rqst_rdma (rqstp); -struct svc_rdma_chunk_ctxt *cc = &head->rc_cc; -unsigned int nr_data_pages, bvec_idx; -struct svc_rdma_rw_ctxt *ctxt; -unsigned int len_left; -int ret; - - nr_data_pages =PAGE_ALIGN (segment->rs_length) >>PAGE_SHIFT ; -if (head->rc_curpage + nr_data_pages > rqstp->rq_maxpages) -return -ENOMEM ; - - ctxt =svc_rdma_get_rw_ctxt (rdma, nr_data_pages); -if (!ctxt) -return -ENOMEM ; - - bvec_idx =0 ; - len_left = segment->rs_length; -while (len_left) { -unsigned int pages_left =PAGE_ALIGN (len_left) >>PAGE_SHIFT ; -unsigned int chunk_len =0 ; - -if (pages_left >=2 ) - chunk_len =svc_rdma_fill_contig_bvec (rqstp, head, - &ctxt->rw_bvec[bvec_idx], - pages_left, len_left); -if (chunk_len) { - bvec_idx++; - }else { -struct page *cur = - rqstp->rq_pages[head->rc_curpage]; - chunk_len =svc_rdma_fill_page_bvec (head, ctxt, cur, - &bvec_idx, - len_left); - } - - len_left -= chunk_len; - } - - ctxt->rw_nents = bvec_idx; - - head->rc_pageoff =offset_in_page (segment->rs_length); -if (head->rc_pageoff) - head->rc_curpage--; - - ret =svc_rdma_rw_ctx_init (rdma, ctxt, segment->rs_offset, - segment->rs_handle, segment->rs_length, -DMA_FROM_DEVICE ); -if (ret <0 ) -return -EIO ; -percpu_counter_inc (&svcrdma_stat_read); - -list_add (&ctxt->rw_list, &cc->cc_rwctxts); - cc->cc_sqecount += ret; -return 0 ; -} -/** * svc_rdma_build_read_segment - Build RDMA Read WQEs to pull one RDMA segment * @rqstp: RPC transaction context
@@ -990,14 +780,6 @@ static int svc_rdma_build_read_segment(sif (check_add_overflow (head->rc_pageoff, len, &total))return -EINVAL ; nr_bvec =PAGE_ALIGN (total) >>PAGE_SHIFT ; - -if (head->rc_pageoff ==0 && nr_bvec >=2 ) { - ret =svc_rdma_build_read_segment_contig (rqstp, head, - segment); -if (ret != -ENOMEM ) -return ret; - } - ctxt =svc_rdma_get_rw_ctxt (rdma, nr_bvec);if (!ctxt)return -ENOMEM ;
@@ -1343,11 +1125,6 @@ static void svc_rdma_clear_rqst_pages(st {unsigned int i; -/* -* Move only pages containing RPC data into rc_pages[]. Pages -* from a contiguous allocation that were not used for the -* payload remain in rq_pages[] for subsequent reuse. -*/ for (i =0 ; i < head->rc_page_count; i++) { head->rc_pages[i] = rqstp->rq_pages[i]; rqstp->rq_pages[i] =NULL ;
Greg Kroah-Hartman
maintainerPATCH 469/518 fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sebastian Alba Vives <[email protected]>
commit fc3b071a7c8dc0f5d56defddf6e6fd5aaa3e1e27 upstream.
afu_ioctl_dma_map() accepts a 64-bit length from userspace via DFL_FPGA_PORT_DMA_MAP ioctl without an upper bound check. The value is passed to afu_dma_pin_pages() where npages is derived as length >> PAGE_SHIFT and passed to pin_user_pages_fast() which takes int nr_pages, causing implicit truncation if length is very large.
Validate map.length at the ioctl entry point before calling afu_dma_map_region(), rejecting values whose page count exceeds INT_MAX.
--- drivers/fpga/dfl-afu-main.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/fpga/dfl-afu-main.c +3 -0
@@ -723,6 +723,9 @@ afu_ioctl_dma_map(struct dfl_feature_devif (map.argsz < minsz || map.flags)return -EINVAL ; +if (map.length >>PAGE_SHIFT > (u64 )INT_MAX ) +return -EINVAL ; + ret =afu_dma_map_region (fdata, map.user_addr, map.length, &map.iova);if (ret)return ret;
Greg Kroah-Hartman
maintainerPATCH 470/518 i2c: core: fix adapter probe deferral loop
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 158efa411c57111d87bf265a3776614f32d70007 upstream.
Drivers must not probe defer after having registered devices as that will trigger a probe loop if the devices bind to a driver (cf. commit fbc35b45f9f6 ("Add documentation on meaning of -EPROBE_DEFER")).
Move the recovery initialisation, where the GPIO lookup may fail, before registering the adapter to prevent this.
--- drivers/i2c/i2c-core-base.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
drivers/i2c/i2c-core-base.c +4 -4
@@ -1561,6 +1561,10 @@ static int i2c_register_adapter(struct i adap->dev.type = &i2c_adapter_type;device_initialize (&adap->dev); + res =i2c_init_recovery (adap); +if (res == -EPROBE_DEFER ) + goto err_put_adap; +/* * This adapter can be used as a parent immediately after device_add(), * setup runtime-pm (especially ignore-children) before hand.
@@ -1587,10 +1591,6 @@ static int i2c_register_adapter(struct iif (res) goto out_reg; - res =i2c_init_recovery (adap); -if (res == -EPROBE_DEFER ) - goto out_reg; -dev_dbg (&adap->dev,"adapter [%s] registered\n" , adap->name);/* create pre-declared device nodes */
Greg Kroah-Hartman
maintainerPATCH 471/518 i2c: core: fix irq domain leak on adapter registration failure
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 8ce19524e4cc2462685f596a6402fbd8fb984ab2 upstream.
Make sure to tear down the host notify irq domain on adapter registration failure to avoid leaking it.
This issue was flagged by Sashiko when reviewing another adapter registration fix.
--- drivers/i2c/i2c-core-base.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/i2c/i2c-core-base.c +3 -1
@@ -1582,7 +1582,7 @@ static int i2c_register_adapter(struct iif (res) {pr_err ("adapter '%s': can't register device (%d)\n" , adap->name, res);put_device (&adap->dev); - goto out_list; + goto err_remove_irq_domain; } adap->debugfs =debugfs_create_dir (dev_name (&adap->dev), i2c_debugfs_root);
@@ -1614,6 +1614,8 @@ out_reg:init_completion (&adap->dev_released);device_unregister (&adap->dev);wait_for_completion (&adap->dev_released); +err_remove_irq_domain: +i2c_host_notify_irq_teardown (adap); out_list:mutex_lock (&core_lock);idr_remove (&i2c_adapter_idr, adap->nr);
Greg Kroah-Hartman
maintainerPATCH 472/518 i2c: core: fix NULL-deref on adapter registration failure
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 2295d2bb101faa663fbc45fadbb3fec45f107441 upstream.
If adapter registration ever fails the release callback would trigger a NULL-pointer dereference as the completion struct has not been initialised.
Note that before the offending commit this would instead have resulted in a minor memory leak of the adapter name.
--- drivers/i2c/i2c-core-base.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
drivers/i2c/i2c-core-base.c +5 -4
@@ -1581,8 +1581,7 @@ static int i2c_register_adapter(struct i res =device_add (&adap->dev);if (res) {pr_err ("adapter '%s': can't register device (%d)\n" , adap->name, res); -put_device (&adap->dev); - goto err_remove_irq_domain; + goto err_put_adap; } adap->debugfs =debugfs_create_dir (dev_name (&adap->dev), i2c_debugfs_root);
@@ -1611,10 +1610,12 @@ static int i2c_register_adapter(struct i out_reg:i2c_deregister_clients (adap);debugfs_remove_recursive (adap->debugfs); +device_del (&adap->dev); +err_put_adap:init_completion (&adap->dev_released); -device_unregister (&adap->dev); +put_device (&adap->dev);wait_for_completion (&adap->dev_released); -err_remove_irq_domain: +i2c_host_notify_irq_teardown (adap); out_list:mutex_lock (&core_lock);
Greg Kroah-Hartman
maintainerPATCH 473/518 i2c: core: fix adapter debugfs creation
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit 07d5fb537928aad4369aaff0cbae73ba38a719af upstream.
Clients can be registered from bus notifier callbacks so the debugfs directory needs to be created before registering the adapter as clients use that directory as their debugfs parent.
Move debugfs creation before adapter registration to avoid having clients create their debugfs directories in the debugfs root (which is also more likely to fail due to name collisions).
Note that failure to allocate the adapter name must now be handled explicitly as debugfs_create_dir() cannot handle a NULL name (unlike device_add() which returns an error).
--- drivers/i2c/i2c-core-base.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/i2c/i2c-core-base.c +10 -6
@@ -1556,7 +1556,10 @@ static int i2c_register_adapter(struct i goto out_list; } -dev_set_name (&adap->dev,"i2c-%d" , adap->nr); + res =dev_set_name (&adap->dev,"i2c-%d" , adap->nr); +if (res) + goto err_remove_irq_domain; + adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type;device_initialize (&adap->dev);
@@ -1578,14 +1581,14 @@ static int i2c_register_adapter(struct iidr_replace (&i2c_adapter_idr, adap, adap->nr);mutex_unlock (&core_lock); + adap->debugfs =debugfs_create_dir (dev_name (&adap->dev), i2c_debugfs_root); + res =device_add (&adap->dev);if (res) {pr_err ("adapter '%s': can't register device (%d)\n" , adap->name, res); - goto err_put_adap; + goto err_remove_debugfs; } - adap->debugfs =debugfs_create_dir (dev_name (&adap->dev), i2c_debugfs_root); - res =i2c_setup_smbus_alert (adap);if (res) goto out_reg;
@@ -1609,13 +1612,14 @@ static int i2c_register_adapter(struct i out_reg:i2c_deregister_clients (adap); -debugfs_remove_recursive (adap->debugfs);device_del (&adap->dev); +err_remove_debugfs: +debugfs_remove_recursive (adap->debugfs); err_put_adap:init_completion (&adap->dev_released);put_device (&adap->dev);wait_for_completion (&adap->dev_released); - +err_remove_irq_domain:i2c_host_notify_irq_teardown (adap); out_list:mutex_lock (&core_lock);
Greg Kroah-Hartman
maintainerPATCH 474/518 i2c: core: fix adapter deregistration race
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Johan Hovold <[email protected]>
commit b1a58ed9eab146b36f41a55db8f5d7ce9fdedf3f upstream.
Adapters can be looked up by their id using i2c_get_adapter() which takes a reference to the embedded struct device.
Remove the adapter from the IDR before tearing it down during deregistration (and on registration failure) to make sure its resources are not accessed after having been freed (e.g. the device name).
--- drivers/i2c/i2c-core-base.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/i2c/i2c-core-base.c +7 -2
@@ -1586,7 +1586,7 @@ static int i2c_register_adapter(struct i res =device_add (&adap->dev);if (res) {pr_err ("adapter '%s': can't register device (%d)\n" , adap->name, res); - goto err_remove_debugfs; + goto err_replace_id; } res =i2c_setup_smbus_alert (adap);
@@ -1613,7 +1613,10 @@ static int i2c_register_adapter(struct i out_reg:i2c_deregister_clients (adap);device_del (&adap->dev); -err_remove_debugfs: +err_replace_id: +mutex_lock (&core_lock); +idr_replace (&i2c_adapter_idr,NULL , adap->nr); +mutex_unlock (&core_lock);debugfs_remove_recursive (adap->debugfs); err_put_adap:init_completion (&adap->dev_released);
@@ -1802,6 +1805,8 @@ void i2c_del_adapter(struct i2c_adapter/* First make sure that this adapter was ever added */ mutex_lock (&core_lock); found =idr_find (&i2c_adapter_idr, adap->nr); +if (found == adap) +idr_replace (&i2c_adapter_idr,NULL , adap->nr);mutex_unlock (&core_lock);if (found != adap) {pr_debug ("attempting to delete unregistered adapter [%s]\n" , adap->name);
Greg Kroah-Hartman
maintainerPATCH 475/518 i2c: mpc: Fix timeout calculations
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Andy Shevchenko <[email protected]>
commit 2e9a7f68329be41792c0b123c28e6c53c2fa2249 upstream.
At first glance the harmless cleanup of the driver does nothing bad. However, as the operator precedence list states the '*' (multiplication) and '/' division operators have order 5 with left-to-right associativity the *= has order 17 and associativity right-to-left. It wouldn't be a problem to replace
foo = foo * HZ / 1000000;
with
foo *= HZ / 1000000;
if HZ constant is in Hertz. The problem is that in the Linux kernel HZ is defined in jiffy units, which is order of magnitude smaller than a million. That's why operator precedence has a crucial role here. Fix the regression by reverting pre-optimized calculations.
--- drivers/i2c/busses/i2c-mpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-mpc.c +1 -1
@@ -844,7 +844,7 @@ static int fsl_i2c_probe(struct platform "fsl,timeout", &mpc_ops.timeout);if (!result) { - mpc_ops.timeout *=HZ /1000000 ; + mpc_ops.timeout = mpc_ops.timeout *HZ /1000000 ;if (mpc_ops.timeout <5 ) mpc_ops.timeout =5 ; }else {
Greg Kroah-Hartman
maintainerPATCH 476/518 i2c: davinci: Unregister cpufreq notifier on probe failure
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
commit e43f32816a1b1fe5a86279411626fe3a9be56d45 upstream.
davinci_i2c_probe() registers a cpufreq transition notifier before adding the I2C adapter. If i2c_add_numbered_adapter() fails, the probe error path releases the device resources without unregistering the notifier.
Add a dedicated error path to unregister the cpufreq notifier after i2c_add_numbered_adapter() fails.
--- drivers/i2c/busses/i2c-davinci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-davinci.c +3 -1
@@ -818,12 +818,14 @@ static int davinci_i2c_probe(struct plat adap->nr = pdev->id; r =i2c_add_numbered_adapter (adap);if (r) - goto err_unuse_clocks; + goto err_cpufreq;pm_runtime_put_autosuspend (dev->dev);return 0 ; +err_cpufreq: +i2c_davinci_cpufreq_deregister (dev); err_unuse_clocks:pm_runtime_dont_use_autosuspend (dev->dev);pm_runtime_put_sync (dev->dev);
Greg Kroah-Hartman
maintainerPATCH 477/518 i2c: stm32f7: truncate clock period instead of rounding it
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guillermo Rodríguez <[email protected]>
commit 111bb7f9f4a90b32e495d70a607c67b137f3074a upstream.
stm32f7_i2c_compute_timing() derives the I2C clock source period (i2cclk) with DIV_ROUND_CLOSEST, which may round it up. When the period is overestimated, all timings computed from it (SCLDEL, SDADEL, SCLL, SCLH) come out shorter on the wire than calculated, and the resulting bus rate can exceed the requested speed, violating the I2C specification minimums for tLOW and tHIGH.
For example, with a 104.45 MHz clock source (e.g. PCLK1, the reset-default I2C clock source on STM32MP1), i2cclk is rounded from 9.574 ns up to 10 ns. Requesting a 400 kHz fast mode bus with 72/27 ns rise/fall times and no analog/digital filters then produces an actual bus rate of 415.6 kHz with tLOW = 1254 ns, violating both the 400 kHz maximum rate and the 1300 ns tLOW minimum of the specification.
Truncate the period instead, so that it can only be underestimated. The error then falls on the safe side: the programmed timings come out slightly longer than computed and the bus runs marginally below the target rate (375.3 kHz in the example above) while meeting the specification.
i2cbus is left rounded-to-closest: it is only used as the target of the clk_error comparison and is never multiplied into the programmed timings, so nearest rounding remains accurate there.
--- drivers/i2c/busses/i2c-stm32f7.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
drivers/i2c/busses/i2c-stm32f7.c +7 -2
@@ -464,8 +464,13 @@ static int stm32f7_i2c_compute_timing(st {struct stm32f7_i2c_spec *specs;u32 p_prev =STM32F7_PRESC_MAX ; -u32 i2cclk =DIV_ROUND_CLOSEST (NSEC_PER_SEC , - setup->clock_src); +/* +* Truncate instead of rounding to closest: if the clock period is +* overestimated, the computed SCL timings will come out shorter on +* the wire, which can push the bus above the target rate and below +* the spec's tLOW/tHIGH minimums. +*/ +u32 i2cclk =NSEC_PER_SEC / setup->clock_src;u32 i2cbus =DIV_ROUND_CLOSEST (NSEC_PER_SEC , setup->speed_freq);u32 clk_error_prev = i2cbus;
Greg Kroah-Hartman
maintainerPATCH 478/518 i2c: imx-lpi2c: mark I2C adapter when hardware is powered down
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Carlos Song <[email protected]>
commit 218cfe364b55b2768221629bd4a69ad190b7fbbc upstream.
On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers.
During system suspend/resume, there exists a time window between:
- suspend_noirq and the system entering suspend
- the system starting to resume and resume_noirq
In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored.
If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang.
Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available.
--- drivers/i2c/busses/i2c-imx-lpi2c.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-imx-lpi2c.c +14 -1
@@ -1646,7 +1646,18 @@ static int __maybe_unused lpi2c_runtime_static int __maybe_unused lpi2c_suspend_noirq (struct device *dev) { -return pm_runtime_force_suspend (dev); +struct lpi2c_imx_struct *lpi2c_imx =dev_get_drvdata (dev); +int ret; + +i2c_mark_adapter_suspended (&lpi2c_imx->adapter); + + ret =pm_runtime_force_suspend (dev); +if (ret) { +i2c_mark_adapter_resumed (&lpi2c_imx->adapter); +return ret; + } + +return 0 ; }static int __maybe_unused lpi2c_resume_noirq (struct device *dev)
@@ -1666,6 +1677,8 @@ static int __maybe_unused lpi2c_resume_nif (lpi2c_imx->target)lpi2c_imx_target_init (lpi2c_imx); +i2c_mark_adapter_resumed (&lpi2c_imx->adapter); +return 0 ; }
Greg Kroah-Hartman
maintainerPATCH 479/518 i2c: i801: fix hardware state machine corruption in error path
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mingyu Wang <[email protected]>
commit 10dd1a736d557e310a77117832874729a0175d57 upstream.
A severe livelock and subsequent Hung Task panic were observed in the i2c-i801 driver during concurrent Fuzzing. The crash is caused by an unconditional hardware register cleanup in the error handling path of i801_access().
When i801_check_pre() fails (e.g., returning -EBUSY because the SMBus controller is actively used by BIOS/ACPI), the kernel does not actually acquire the hardware ownership. However, the code jumps to the 'out' label and executes:
iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv));
This forcefully clears the INUSE_STS lock and resets the hardware status flags without owning the controller. Doing so interrupts ongoing BIOS/ACPI transactions and totally corrupts the SMBus hardware state machine.
Consequently, all subsequent i801_access() calls fail at the pre-check stage, triggering an endless stream of "SMBus is busy, can't use it!" error logs. Over a slow serial console, this printk flood monopolizes the CPU (Console Livelock), starving other processes trying to acquire the mmap_lock down_read semaphore, ultimately triggering the hung task watchdog.
Fix this by moving the 'out' label below the hardware register cleanup. If i801_check_pre() fails, we safely bypass the iowrite8() and only release the software locks (pm_runtime and mutex), strictly adhering to the rule of not releasing resources that were never acquired.
--- drivers/i2c/busses/i2c-i801.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-i801.c +1 -1
@@ -931,13 +931,13 @@ static s32 i801_access(struct i2c_adapte*/ if (hwpec)iowrite8 (ioread8 (SMBAUXCTL (priv)) & ~SMBAUXCTL_CRC ,SMBAUXCTL (priv)); -out:/* * Unlock the SMBus device for use by BIOS/ACPI, * and clear status flags if not done already. */ iowrite8 (SMBHSTSTS_INUSE_STS |STATUS_FLAGS ,SMBHSTSTS (priv)); +out:pm_runtime_put_autosuspend (&priv->pci_dev->dev);mutex_unlock (&priv->acpi_lock);return ret;
Greg Kroah-Hartman
maintainerPATCH 480/518 Input: synaptics-rmi4 - unregister function handlers on physical driver registration failure
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Haoxiang Li <[email protected]>
commit 6251f7d3472c0409e30f8d6a24f10d33d12e3f9a upstream.
If rmi_register_physical_driver() fails, the current error path unregisters only the RMI bus. The function handlers registered earlier remain registered with the driver core.
Add a separate error path to unregister the function handlers before unregistering the bus in this failure case.
--- drivers/input/rmi4/rmi_bus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/input/rmi4/rmi_bus.c +3 -1
@@ -455,11 +455,13 @@ static int __init rmi_bus_init(void)if (error) {pr_err ("%s: error registering the RMI physical driver: %d\n" , __func__, error); - goto err_unregister_bus; + goto err_unregister_function_handlers; }return 0 ; +err_unregister_function_handlers: +rmi_unregister_function_handlers (); err_unregister_bus:bus_unregister (&rmi_bus_type);return error;
Greg Kroah-Hartman
maintainerPATCH 481/518 Input: synaptics-rmi4 - bound the F3A keymap to the GPIO count
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 57c10915f2c16c90e0d46ad00876bf39ece40fc2 upstream.
rmi_f3a_initialize() takes the GPIO count from the device query register (f3a->gpio_count = buf & RMI_F3A_GPIO_COUNT, range 0..127). rmi_f3a_map_gpios() then allocates gpio_key_map with min(gpio_count, TRACKSTICK_RANGE_END) == at most 6 entries, but rmi_f3a_attention() iterates the full gpio_count and dereferences gpio_key_map[i], and input->keycodemax is set to the full gpio_count while input->keycode points at the 6-entry allocation.
A device that reports gpio_count > 6 therefore causes an out-of-bounds read of gpio_key_map[] on every attention interrupt, and out-of-bounds accesses through the input core's default keymap ioctls: EVIOCGKEYCODE reads past the buffer (leaking adjacent slab memory to user space) and EVIOCSKEYCODE writes a caller-controlled value past it, for any process able to open the evdev node, since input_default_getkeycode() and input_default_setkeycode() only bound the index against keycodemax.
Size the keymap for the full gpio_count. The mapping loop is unchanged:
it still assigns only the first min(gpio_count, TRACKSTICK_RANGE_END) entries; the remaining slots stay KEY_RESERVED (devm_kcalloc zero-fills) and are skipped when reporting.
--- drivers/input/rmi4/rmi_f3a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/rmi4/rmi_f3a.c +1 -1
@@ -132,7 +132,7 @@ static int rmi_f3a_map_gpios(struct rmi_int button_count =min_t (u8, f3a->gpio_count,TRACKSTICK_RANGE_END ); f3a->gpio_key_map =devm_kcalloc (&fn->dev, - button_count, + f3a->gpio_count,sizeof (f3a->gpio_key_map[0 ]),GFP_KERNEL );if (!f3a->gpio_key_map) {
Greg Kroah-Hartman
maintainerPATCH 482/518 Input: synaptics-rmi4 - bound the F30 keymap to the GPIO/LED count
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit d577e46785d45484b2ab7e7309c49b18764bf56c upstream.
rmi_f30_map_gpios() allocates gpioled_key_map with min(gpioled_count, TRACKSTICK_RANGE_END) == at most 6 entries, but rmi_f30_attention() iterates the full f30->gpioled_count (device query register, range 0..31) and dereferences gpioled_key_map[i], and input->keycodemax is set to the full gpioled_count while input->keycode points at the 6-entry allocation.
A device that reports gpioled_count > 6 with GPIO support enabled therefore causes an out-of-bounds read on the attention interrupt and out-of-bounds read/write through the EVIOCGKEYCODE/EVIOCSKEYCODE ioctls, which bound the index only against keycodemax. This is the same defect as the F3A handler, which was copied from F30.
Size the keymap for the full gpioled_count; the mapping loop still assigns only the first min(gpioled_count, TRACKSTICK_RANGE_END) entries.
--- drivers/input/rmi4/rmi_f30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/rmi4/rmi_f30.c +1 -1
@@ -233,7 +233,7 @@ static int rmi_f30_map_gpios(struct rmi_int button_count =min_t (u8, f30->gpioled_count,TRACKSTICK_RANGE_END ); f30->gpioled_key_map =devm_kcalloc (&fn->dev, - button_count, + f30->gpioled_count,sizeof (f30->gpioled_key_map[0 ]),GFP_KERNEL );if (!f30->gpioled_key_map) {
Greg Kroah-Hartman
maintainerPATCH 483/518 Input: elan_i2c - prevent division by zero and arithmetic underflow
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ranjan Kumar <[email protected]>
commit df2b818fa009c10ff6ba875a1663ff001cda9558 upstream.
The Elan I2C touchpad driver queries the device for its physical dimensions and trace counts to calculate the device resolution and width. However, if the device firmware or device tree provides invalid zero values for x_traces or y_traces, it results in a fatal division-by-zero exception leading to a kernel panic during device probe.
Add checks to ensure these parameters are non-zero before performing the division. If invalid trace values are detected, fall back to a safe default of 1.
Additionally, prevent an arithmetic underflow in the touch reporting logic. Previously, if the calculated or fallback width was smaller than ETP_FWIDTH_REDUCE (90), the subtraction would underflow, resulting in a massive unsigned integer being reported to userspace. Clamp the adjusted width to a minimum of 0 to safely handle small physical dimensions and fallback scenarios.
Completing the probe with safe fallback values ensures the sysfs nodes are created, keeping the firmware update path intact so a recovery firmware can be flashed to the device.
--- drivers/input/mouse/elan_i2c_core.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-)
drivers/input/mouse/elan_i2c_core.c +30 -6
@@ -428,8 +428,17 @@ static int elan_query_device_parameters(if (error)return error; } - data->width_x = data->max_x / x_traces; - data->width_y = data->max_y / y_traces; + +if (!x_traces || !y_traces) { +dev_warn (&client->dev, +"invalid trace numbers: x=%u, y=%u\n" , + x_traces, y_traces); + data->width_x =1 ; + data->width_y =1 ; + }else { + data->width_x = data->max_x / x_traces; + data->width_y = data->max_y / y_traces; + } if (device_property_read_u32(&client->dev, "touchscreen-x-mm", &x_mm) ||
@@ -443,8 +452,16 @@ static int elan_query_device_parameters( data->x_res =elan_convert_resolution (hw_x_res, data->pattern); data->y_res =elan_convert_resolution (hw_y_res, data->pattern); }else { - data->x_res = (data->max_x +1 ) / x_mm; - data->y_res = (data->max_y +1 ) / y_mm; +if (unlikely (x_mm ==0 || y_mm ==0 )) { +dev_warn (&client->dev, +"invalid physical dimensions: x_mm=%u, y_mm=%u\n" , + x_mm, y_mm); + data->x_res =1 ; + data->y_res =1 ; + }else { + data->x_res = (data->max_x +1 ) / x_mm; + data->y_res = (data->max_y +1 ) / y_mm; + } }if (device_property_read_bool (&client->dev,"elan,clickpad" ))
@@ -956,6 +973,7 @@ static void elan_report_contact(struct eif (data->report_features &ETP_FEATURE_REPORT_MK ) {unsigned int mk_x, mk_y, area_x, area_y; +int adj_width_x, adj_width_y;u8 mk_data = high_precision ? packet[ETP_MK_DATA_OFFSET + contact_num] : finger_data[3 ];
@@ -967,8 +985,14 @@ static void elan_report_contact(struct e* To avoid treating large finger as palm, let's reduce * the width x and y per trace. */ - area_x = mk_x * (data->width_x -ETP_FWIDTH_REDUCE ); - area_y = mk_y * (data->width_y -ETP_FWIDTH_REDUCE ); + + adj_width_x = data->width_x >ETP_FWIDTH_REDUCE ? + data->width_x -ETP_FWIDTH_REDUCE :0 ; + adj_width_y = data->width_y >ETP_FWIDTH_REDUCE ? + data->width_y -ETP_FWIDTH_REDUCE :0 ; + + area_x = mk_x * adj_width_x; + area_y = mk_y * adj_width_y;input_report_abs (input,ABS_TOOL_WIDTH , mk_x); input_report_abs(input, ABS_MT_TOUCH_MAJOR,
Greg Kroah-Hartman
maintainerPATCH 484/518 Input: goodix - clamp the device-reported contact count
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 5ed62a96e06be4e94b8296b7932afee550a70e04 upstream.
goodix_ts_read_input_report() copies the number of touch points reported by the device into an on-stack buffer
u8 point_data[2 + GOODIX_MAX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
which is sized for at most GOODIX_MAX_CONTACTS (10) contacts. The only runtime check bounds the per-interrupt count against ts->max_touch_num, but that value is taken verbatim from a 4-bit field of the device configuration block and is never clamped:
ts->max_touch_num = ts->config[MAX_CONTACTS_LOC] & 0x0f;
The nibble can be 0..15, so a malfunctioning, malicious or counterfeit controller (or an attacker tampering with the I2C bus) can advertise up to 15 contacts. goodix_ts_read_input_report() then accepts a touch_num of up to 15 and the second goodix_i2c_read() writes ts->contact_size * (touch_num - 1) bytes past the one-contact header into point_data - up to 30 bytes (45 with the 9-byte report format) beyond the 92-byte buffer: a stack out-of-bounds write.
Clamp max_touch_num to GOODIX_MAX_CONTACTS, the number of contacts point_data[] is sized for, when reading it from the configuration.
--- drivers/input/touchscreen/goodix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/input/touchscreen/goodix.c +2 -1
@@ -1057,7 +1057,8 @@ static void goodix_read_config(struct go } ts->int_trigger_type = ts->config[TRIGGER_LOC ] &0x03 ; - ts->max_touch_num = ts->config[MAX_CONTACTS_LOC ] &0x0f ; + ts->max_touch_num =min (ts->config[MAX_CONTACTS_LOC ] &0x0f , +GOODIX_MAX_CONTACTS ); x_max =get_unaligned_le16 (&ts->config[RESOLUTION_LOC ]); y_max =get_unaligned_le16 (&ts->config[RESOLUTION_LOC +2 ]);
Greg Kroah-Hartman
maintainerPATCH 485/518 Input: iforce - bound the device-reported force-feedback effect index
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 0e9943d2e4c63496b6ca84bc66fd3c71d40558e2 upstream.
iforce_process_packet() handles a status report (packet id 0x02) by taking a force-feedback effect index straight from the device wire and using it to address the per-effect state array:
i = data[1] & 0x7f;
if (data[1] & 0x80) {
if (!test_and_set_bit(FF_CORE_IS_PLAYED,
iforce->core_effects[i].flags))
...
} else if (test_and_clear_bit(FF_CORE_IS_PLAYED,
iforce->core_effects[i].flags)) {
...
}The index is masked only with 0x7f, so it ranges 0..127, but core_effects[] holds only IFORCE_EFFECTS_MAX (32) entries. For an index of 32..127 the test_and_set_bit()/test_and_clear_bit() is an out-of-bounds single-bit read-modify-write past the array. core_effects[] is the second-to-last member of struct iforce, so the write lands in the trailing members and beyond the embedding kzalloc()'d iforce_serio / iforce_usb object.
data[1] is unvalidated device payload on both transports (the USB interrupt endpoint and serio), and the status path is not gated on force feedback being present, so a malicious or counterfeit device can set or clear a bit at an attacker-chosen offset past the object.
Reject an out-of-range index instead of indexing with it. Bound against the array dimension IFORCE_EFFECTS_MAX rather than dev->ff->max_effects so the check guarantees memory safety regardless of how many effects the device registered. A legitimate "effect started/stopped" status always carries an index below IFORCE_EFFECTS_MAX, so well-formed devices are unaffected; the neighbouring mark_core_as_ready() loop is already bounded and is left untouched.
--- drivers/input/joystick/iforce/iforce-packets.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
drivers/input/joystick/iforce/iforce-packets.c +11 -7
@@ -186,14 +186,18 @@ void iforce_process_packet(struct iforce/* Check if an effect was just started or stopped */ i = data[1 ] &0x7f ; -if (data[1 ] &0x80 ) { -if (!test_and_set_bit (FF_CORE_IS_PLAYED , iforce->core_effects[i].flags)) { -/* Report play event */ -input_report_ff_status (dev, i,FF_STATUS_PLAYING ); +if (i <IFORCE_EFFECTS_MAX ) { +if (data[1 ] &0x80 ) { +if (!test_and_set_bit (FF_CORE_IS_PLAYED , + iforce->core_effects[i].flags)) { +/* Report play event */ +input_report_ff_status (dev, i,FF_STATUS_PLAYING ); + } + }else if (test_and_clear_bit (FF_CORE_IS_PLAYED , + iforce->core_effects[i].flags)) { +/* Report stop event */ +input_report_ff_status (dev, i,FF_STATUS_STOPPED ); } - }else if (test_and_clear_bit (FF_CORE_IS_PLAYED , iforce->core_effects[i].flags)) { -/* Report stop event */ -input_report_ff_status (dev, i,FF_STATUS_STOPPED ); }for (j =3 ; j < len; j +=2 )
Greg Kroah-Hartman
maintainerPATCH 486/518 Input: mms114 - fix touch indexing for MMS134S and MMS136
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit a6ac4e24c1a8a533bb61035184fdcc7eede4cc8d upstream.
The MMS134S and MMS136 touch controllers have an event size of 6 bytes rather than 8 bytes. When __mms114_read_reg() reads the touch data packet from the device into the touch buffer, the events are packed tightly at 6-byte intervals. However, the driver iterates through the events using standard C array indexing (touch[index]), where each element is sizeof(struct mms114_touch) (8 bytes) apart. As a result, any touch events beyond the first one are read from incorrect offsets and parsed improperly.
Fix this by explicitly calculating the byte offset for each touch event based on the device's specific event size.
--- drivers/input/touchscreen/mms114.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
drivers/input/touchscreen/mms114.c +12 -6
@@ -217,7 +217,9 @@ static irqreturn_t mms114_interrupt(intstruct mms114_data *data = dev_id;struct i2c_client *client = data->client;struct mms114_touch touch[MMS114_MAX_TOUCH ]; +struct mms114_touch *t;int packet_size; +int event_size;int touch_size;int index;int error;
@@ -228,9 +230,11 @@ static irqreturn_t mms114_interrupt(int/* MMS136 has slightly different event size */ if (data->type ==TYPE_MMS134S || data->type ==TYPE_MMS136 ) - touch_size = packet_size /MMS136_EVENT_SIZE ; + event_size =MMS136_EVENT_SIZE ;else - touch_size = packet_size /MMS114_EVENT_SIZE ; + event_size =MMS114_EVENT_SIZE ; + + touch_size = packet_size / event_size; error =__mms114_read_reg (data,MMS114_INFORMATION , packet_size, (u8 *)touch);
@@ -238,18 +242,20 @@ static irqreturn_t mms114_interrupt(int goto out;for (index =0 ; index < touch_size; index++) { -switch (touch[index].type) { + t = (struct mms114_touch *)((u8 *)touch + index * event_size); + +switch (t->type) {case MMS114_TYPE_TOUCHSCREEN : -mms114_process_mt (data, touch + index); +mms114_process_mt (data, t);break ;case MMS114_TYPE_TOUCHKEY : -mms114_process_touchkey (data, touch + index); +mms114_process_touchkey (data, t);break ;default :dev_err (&client->dev,"Wrong touch type (%d)\n" , - touch[index].type); + t->type);break ; } }
Greg Kroah-Hartman
maintainerPATCH 487/518 Input: ads7846 - dont use scratch for tx_buf when clearing register
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Kris Bahnsen <[email protected]>
commit 856668312685d9a8f32d49a135a89c429d309f81 upstream.
The workaround for XPT2046 clears the command register, giving the touchscreen controller a NOP. The change incorrectly re-uses the req->scratch variable which is used as rx_buf for xfer[5], so by the time xfer[6] occurs, the contents of req->scratch may not be
0. It was found that the touchscreen controller can end up in a completely unresponsive state due to it being given a command the driver does not expect.
Instead, rely on the spi_transfer behavior of tx_buf being NULL to transmit all 0 bits and use the scratch variable for the rx_buf for both the 1 byte command to and 2 byte response from the controller.
Also relocates the scratch member of struct ser_req to force it into a different cache line to prevent any potential issues of DMA stepping on unrelated data in other struct members due to sharing the same cache line.
This change was tested on real TSC2046 and ADS7843 controllers, but not the XPT2046 the workaround was originally created for. Confirming that the original modification to clear the command register does not impact either real controller.
--- drivers/input/touchscreen/ads7846.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
drivers/input/touchscreen/ads7846.c +2 -3
@@ -325,7 +325,6 @@ struct ser_req {u8 ref_on;u8 command;u8 ref_off; -u16 scratch;struct spi_message msg;struct spi_transfer xfer[8 ];/*
@@ -333,6 +332,7 @@ struct ser_req {* transfer buffers to live in their own cache lines. */ __be16 sample____cacheline_aligned ; +u16 scratch; };struct ads7845_ser_req {
@@ -403,8 +403,7 @@ static int ads7846_read12_ser(struct devspi_message_add_tail (&req->xfer[5 ], &req->msg);/* clear the command register */ - req->scratch =0 ; - req->xfer[6 ].tx_buf = &req->scratch; + req->xfer[6 ].rx_buf = &req->scratch; req->xfer[6 ].len =1 ;spi_message_add_tail (&req->xfer[6 ], &req->msg);
Greg Kroah-Hartman
maintainerPATCH 488/518 Input: touchwin - reset the packet index on every complete packet
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 478cdd736f2ce3114f90e775d7358136d3977b94 upstream.
tw_interrupt() accumulates each non-zero serial byte into a fixed three-byte buffer with a running index that is only reset once a full packet has been received *and* the device's two Y bytes agree:
tw->data[tw->idx++] = data;
if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
...
tw->idx = 0;
}The reset is gated on tw->data[1] == tw->data[2], a value the device controls. A malicious, malfunctioning or counterfeit Touchwindow peripheral can stream non-zero bytes whose 2nd and 3rd bytes differ: the index reaches TW_LENGTH without the equality holding, is never reset, and keeps growing, so tw->data[tw->idx++] walks off the end of the three-byte array and the rest of the heap-allocated struct tw, one attacker-chosen byte at a time -- an unbounded, device-driven heap out-of-bounds write.
Reset the index on every completed packet and report an event only when the two Y bytes match, like the other serio touchscreen drivers do.
--- drivers/input/touchscreen/touchwin.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
drivers/input/touchscreen/touchwin.c +9 -6
@@ -63,12 +63,15 @@ static irqreturn_t tw_interrupt(struct sif (data) {/* touch */ tw->touched =1 ; tw->data[tw->idx++] = data; -/* verify length and that the two Y's are the same */ -if (tw->idx ==TW_LENGTH && tw->data[1 ] == tw->data[2 ]) { -input_report_abs (dev,ABS_X , tw->data[0 ]); -input_report_abs (dev,ABS_Y , tw->data[1 ]); -input_report_key (dev,BTN_TOUCH ,1 ); -input_sync (dev); +/* a full packet ends the accumulation, valid or not */ +if (tw->idx ==TW_LENGTH ) { +/* report only if the two Y's are the same */ +if (tw->data[1 ] == tw->data[2 ]) { +input_report_abs (dev,ABS_X , tw->data[0 ]); +input_report_abs (dev,ABS_Y , tw->data[1 ]); +input_report_key (dev,BTN_TOUCH ,1 ); +input_sync (dev); + } tw->idx =0 ; } }else if (tw->touched) {/* untouch */
Greg Kroah-Hartman
maintainerPATCH 489/518 Input: mms114 - reject an oversized device packet size
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bryam Vargas <[email protected]>
commit 66725039f7090afe14c31bd259e2059a68f04023 upstream.
mms114_interrupt() reads a packet of touch data from the device into a fixed-size on-stack buffer
struct mms114_touch touch[MMS114_MAX_TOUCH];
which holds MMS114_MAX_TOUCH (10) events of MMS114_EVENT_SIZE (8) bytes, i.e. 80 bytes. The length of the I2C read into it is taken verbatim from the device:
packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); if (packet_size <= 0) goto out; ... error = __mms114_read_reg(data, MMS114_INFORMATION, packet_size, (u8 *)touch);
packet_size is a single device register byte (0x0F) and the only check is the lower bound packet_size <= 0; it is never bounded against the size of touch[]. A malfunctioning, malicious or counterfeit controller (or an attacker tampering with the I2C bus) can report a packet_size of up to 255, so __mms114_read_reg() writes up to 175 bytes past the end of touch[] on the IRQ-thread stack: a stack out-of-bounds write that can overwrite the stack canary, saved registers and the return address.
A well-formed device never reports more than the buffer holds, so reject an oversized packet and drop the report, consistent with the handler's other error paths, rather than reading past the buffer.
--- drivers/input/touchscreen/mms114.c | 6 ++++++ 1 file changed, 6 insertions(+)
drivers/input/touchscreen/mms114.c +6 -0
@@ -228,6 +228,12 @@ static irqreturn_t mms114_interrupt(intif (packet_size <=0 ) goto out; +if (packet_size >sizeof (touch)) { +dev_err (&client->dev,"Invalid packet size %d (max %zu)\n" , + packet_size,sizeof (touch)); + goto out; + } +/* MMS136 has slightly different event size */ if (data->type ==TYPE_MMS134S || data->type ==TYPE_MMS136 ) event_size =MMS136_EVENT_SIZE ;
Greg Kroah-Hartman
maintainerPATCH 490/518 Input: gscps2 - advance receive buffer write index
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xu Rao <[email protected]>
commit d86d4f8cbb5a55a3b9b86f7b5ab8c4cdda600a3f upstream.
Commit 44f920069911 ("Input: gscps2 - use guard notation when acquiring spinlock") moved the receive loop into gscps2_read_data() and gscps2_report_data().
While moving the code, it preserved the writes to buffer[ps2port->append], but omitted the following producer index update from the original loop:
ps2port->append = (ps2port->append + 1) & BUFFER_SIZE;
As a result, append never advances. Since gscps2_report_data() only reports bytes while act != append, the receive buffer always appears empty and no keyboard or mouse data reaches the serio core.
Restore the omitted index update.
--- drivers/input/serio/gscps2.c | 1 + 1 file changed, 1 insertion(+)
drivers/input/serio/gscps2.c +1 -0
@@ -219,6 +219,7 @@ static void gscps2_read_data(struct gscp ps2port->buffer[ps2port->append].str = status; ps2port->buffer[ps2port->append].data =gscps2_readb_input (ps2port->addr); + ps2port->append = (ps2port->append +1 ) &BUFFER_SIZE ; }while (true); }
Greg Kroah-Hartman
maintainerPATCH 491/518 Input: maplemouse - fix NULL pointer dereference in open()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Florian Fuchs <[email protected]>
commit ee89db004238bd0b034f2a6176e175561658750b upstream.
Commit 555c765b0cc2 ("Input: mouse - drop unnecessary calls to input_set_drvdata") dropped the input_set_drvdata() call in probe because the data appeared to be unused. However, dc_mouse_open() and dc_mouse_close() were using maple_get_drvdata(to_maple_dev(&dev->dev)). This appears to be accessing the data attached to an instance of maple_device structure, while in reality this actually retrieves driver data from the input device's embedded struct device (doing invalid conversion of input device structure to maple device). After input_set_drvdata() was removed, that lookup started returning NULL and opening the input device dereferences mse->mdev.
Restore input_set_drvdata() and convert open() and close() to use input_get_drvdata() so the dependency is no longer hidden.
--- drivers/input/mouse/maplemouse.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/input/mouse/maplemouse.c +3 -2
@@ -48,7 +48,7 @@ static void dc_mouse_callback(struct mapstatic int dc_mouse_open (struct input_dev *dev) { -struct dc_mouse *mse =maple_get_drvdata (to_maple_dev (&dev->dev)); +struct dc_mouse *mse =input_get_drvdata (dev);maple_getcond_callback (mse->mdev, dc_mouse_callback,HZ /50 ,MAPLE_FUNC_MOUSE );
@@ -58,7 +58,7 @@ static int dc_mouse_open(struct input_destatic void dc_mouse_close (struct input_dev *dev) { -struct dc_mouse *mse =maple_get_drvdata (to_maple_dev (&dev->dev)); +struct dc_mouse *mse =input_get_drvdata (dev);maple_getcond_callback (mse->mdev, dc_mouse_callback,0 ,MAPLE_FUNC_MOUSE );
@@ -88,6 +88,7 @@ static int probe_maple_mouse(struct devi mse->dev = input_dev; mse->mdev = mdev; +input_set_drvdata (input_dev, mse); input_dev->evbit[0 ] =BIT_MASK (EV_KEY ) |BIT_MASK (EV_REL ); input_dev->keybit[BIT_WORD (BTN_MOUSE )] =BIT_MASK (BTN_LEFT ) |BIT_MASK (BTN_RIGHT ) |BIT_MASK (BTN_MIDDLE );
Greg Kroah-Hartman
maintainerPATCH 492/518 Input: mms114 - fix multi-touch slot corruption
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit adea84ee6cdea611146c4251d3c1616f5a09feca upstream.
If the touchscreen controller reports a touch ID of 0, the driver calculates the slot ID as touch->id - 1, which underflows to UINT_MAX. This is passed to input_mt_slot() as -1.
Since the input core ignores negative slot values, the active slot remains unchanged. The driver then reports the touch coordinates for the previously active slot, corrupting its state.
Fix this by rejecting touch reports with ID 0.
--- drivers/input/touchscreen/mms114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/input/touchscreen/mms114.c +1 -1
@@ -165,7 +165,7 @@ static void mms114_process_mt(struct mmsunsigned int x;unsigned int y; -if (touch->id >MMS114_MAX_TOUCH ) { +if (touch->id ==0 || touch->id >MMS114_MAX_TOUCH ) {dev_err (&client->dev,"Wrong touch id (%d)\n" , touch->id);return ; }
Greg Kroah-Hartman
maintainerPATCH 493/518 Input: maple_keyb - set driver data before registering input device
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit 536394ec81419b67d9f4f0028812c4372397be1b upstream.
Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered.
--- drivers/input/keyboard/maple_keyb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/input/keyboard/maple_keyb.c +2 -2
@@ -166,6 +166,8 @@ static int probe_maple_kbd(struct device kbd->dev = idev;memcpy (kbd->keycode, dc_kbd_keycode,sizeof (kbd->keycode)); +maple_set_drvdata (mdev, kbd); + idev->name = mdev->product_name; idev->evbit[0 ] =BIT (EV_KEY ) |BIT (EV_REP ); idev->keycode = kbd->keycode;
@@ -190,8 +192,6 @@ static int probe_maple_kbd(struct device mdev->driver = mdrv; -maple_set_drvdata (mdev, kbd); -return error; fail_register:
Greg Kroah-Hartman
maintainerPATCH 494/518 Input: maplemouse - set driver data before registering input device
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit 738f24bbbc95dd50cb4229d1ed62a05f29db2bda upstream.
Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered.
--- drivers/input/mouse/maplemouse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/input/mouse/maplemouse.c +3 -1
@@ -88,6 +88,8 @@ static int probe_maple_mouse(struct devi mse->dev = input_dev; mse->mdev = mdev; +maple_set_drvdata (mdev, mse); +input_set_drvdata (input_dev, mse); input_dev->evbit[0 ] =BIT_MASK (EV_KEY ) |BIT_MASK (EV_REL ); input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
@@ -103,12 +105,12 @@ static int probe_maple_mouse(struct devi goto fail_register; mdev->driver = mdrv; -maple_set_drvdata (mdev, mse);return error; fail_register:input_free_device (input_dev); +maple_set_drvdata (mdev,NULL ); fail_nomem:kfree (mse); fail:
Greg Kroah-Hartman
maintainerPATCH 495/518 Input: maplecontrol - set driver data before registering input device
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Dmitry Torokhov <[email protected]>
commit fe938ee497d58c644f6910cfe6ae155f6fb3e523 upstream.
Set maple driver data before calling input_register_device() to ensure that it is available if the device is opened immediately and the callback is triggered.
--- drivers/input/joystick/maplecontrol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
drivers/input/joystick/maplecontrol.c +2 -1
@@ -112,6 +112,8 @@ static int probe_maple_controller(struct pad->dev = idev; pad->mdev = mdev; +maple_set_drvdata (mdev, pad); + idev->open = dc_pad_open; idev->close = dc_pad_close;
@@ -146,7 +148,6 @@ static int probe_maple_controller(struct goto fail; mdev->driver = mdrv; -maple_set_drvdata (mdev, pad);return 0 ;
Greg Kroah-Hartman
maintainerPATCH 496/518 RDMA/rtrs-srv: Bound RDMA-Write length to chunk size in rdma_write_sg
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhenhao Wan <[email protected]>
commit 963af8d97a8c6a117134a8d0db1415e0489200b1 upstream.
When the server answers an RTRS READ, rdma_write_sg() builds the source scatter/gather entry for the IB_WR_RDMA_WRITE that returns data to the peer. Its length is taken directly from the wire descriptor:
plist->length = le32_to_cpu(id->rd_msg->desc[0].len);
rd_msg points into the chunk buffer that the remote peer filled via RDMA-WRITE-WITH-IMM (rtrs_srv_rdma_done() -> process_io_req() -> process_read()), so desc[0].len is attacker-controlled and, before this change, was only rejected when zero. The source address is the fixed chunk start (dma_addr[msg_id]) and the source lkey is the PD-wide local_dma_lkey, which is not tied to the chunk's MR mapping, so the verbs layer does not constrain the transfer length to max_chunk_size. msg_id and off are bounded against queue_depth and max_chunk_size in rtrs_srv_rdma_done(), but desc[0].len is a separate field that was not checked against the chunk size.
A peer that advertises desc[0].len larger than max_chunk_size can make the posted RDMA write read past the chunk's mapped region. The resulting behaviour depends on the IOMMU configuration: with no IOMMU or in passthrough mode the read may extend into memory adjacent to the chunk and be returned to the peer, which can disclose host memory; with a translating IOMMU the out-of-range access is expected to fault and abort the connection. In either case the transfer exceeds what the protocol permits and is driven by a remote peer.
Reject a descriptor length above max_chunk_size, mirroring the existing off >= max_chunk_size bound in rtrs_srv_rdma_done(). Legitimate clients do not exceed it: the client sets desc[0].len to its MR length, which is capped at the negotiated max_io_size (max_chunk_size - MAX_HDR_SIZE).
--- drivers/infiniband/ulp/rtrs/rtrs-srv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/infiniband/ulp/rtrs/rtrs-srv.c +3 -2
@@ -225,8 +225,9 @@ static int rdma_write_sg(struct rtrs_srv/* WR will fail with length error * if this is 0 */ -if (plist->length ==0 ) { -rtrs_err (s,"Invalid RDMA-Write sg list length 0\n" ); +if (plist->length ==0 || plist->length > max_chunk_size) { +rtrs_err (s,"Invalid RDMA-Write sg list length %u\n" , + plist->length);return -EINVAL ; }
Greg Kroah-Hartman
maintainerPATCH 497/518 RDMA/core: Fix broadcast address falsely detected as local
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Maher Sanalla <[email protected]>
commit 942cd47faa2047b46dfd85745603eba9006973e6 upstream.
When rdma_resolve_addr() is invoked with a broadcast destination on an IPoIB interface, is_dst_local() inspects the resolved route and incorrectly concludes that the address is local. As a result, the resolution fails with -ENODEV.
The issue stems from using '&' to compare rt_type with RTN_LOCAL. The RTN_* values form a sequential enum, not a bitmask (RTN_LOCAL=2, RTN_BROADCAST=3). Thus, "rt_type & RTN_LOCAL" yields a non-zero result for a broadcast route as well.
Replace '&' with '==' when comparing rt_type against RTN_LOCAL.
--- drivers/infiniband/core/addr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/infiniband/core/addr.c +1 -1
@@ -438,7 +438,7 @@ static int addr6_resolve(struct sockaddrstatic bool is_dst_local (const struct dst_entry *dst) {if (dst->ops->family ==AF_INET ) -return !!(dst_rtable (dst)->rt_type &RTN_LOCAL ); +return dst_rtable (dst)->rt_type ==RTN_LOCAL ;else if (dst->ops->family ==AF_INET6 )return !!(dst_rt6_info (dst)->rt6i_flags &RTF_LOCAL );else
Greg Kroah-Hartman
maintainerPATCH 498/518 RDMA/siw: bound Read Response placement to the RREAD length
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Michael Bommarito <[email protected]>
commit 7d29f7e9dbd844cae4d3e559cf78324b9642fd6b upstream.
In drivers/infiniband/sw/siw/siw_qp_rx.c, siw_proc_rresp() places each inbound Read Response DDP segment at sge->laddr + wqe->processed and then accumulates wqe->processed, but it never checks the running total against the sink buffer length on continuation segments. siw_check_sge() resolves and validates the sink memory only on the first fragment (the if (!*mem) branch), and siw_rresp_check_ntoh() compares the cumulative length against wqe->bytes only on the final segment (the !frx->more_ddp_segs guard).
A connected siw peer that answers an outstanding RREAD with Read Response segments that keep the DDP Last flag clear, carrying more total payload than the RREAD requested, drives wqe->processed past the validated sink buffer; the next siw_rx_data() call writes out of bounds at sge->laddr + wqe->processed. siw runs iWARP over ordinary routable TCP, so the peer is the remote end of an established RDMA connection and needs no local privilege.
Bound every segment before placement, exactly as siw_proc_send() and siw_proc_write() already do for their tagged and untagged paths, and terminate the connection with a base-or-bounds DDP error when the Read Response would overrun the sink buffer.
This is the second receive-path length fix for this file. A separate change rejects an MPA FPDU length that underflows the per-fragment remainder in the header decode; that guard does not cover this case, because here each individual segment length is self-consistent and only the accumulated placement offset overruns the buffer.
--- drivers/infiniband/sw/siw/siw_qp_rx.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/infiniband/sw/siw/siw_qp_rx.c +9 -0
@@ -844,6 +844,15 @@ int siw_proc_rresp(struct siw_qp *qp) } mem_p = *mem; +if (unlikely (wqe->processed + srx->fpdu_part_rem > wqe->bytes)) { +siw_dbg_qp (qp,"rresp len: %d + %d > %d\n" , + wqe->processed, srx->fpdu_part_rem, wqe->bytes); + wqe->wc_status =SIW_WC_LOC_LEN_ERR ; +siw_init_terminate (qp,TERM_ERROR_LAYER_DDP , +DDP_ETYPE_TAGGED_BUF , +DDP_ECODE_T_BASE_BOUNDS ,0 ); +return -EINVAL ; + } bytes =min (srx->fpdu_part_rem, srx->skb_new); rv =siw_rx_data (mem_p, srx, &frx->pbl_idx, sge->laddr + wqe->processed, bytes);
Greg Kroah-Hartman
maintainerPATCH 499/518 fuse: back uncached readdir buffers with pages
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matthew R. Ochs <[email protected]>
commit 4dd6f6d3085a84e74b0a1efec3a05ed0b5125dce upstream.
Commit dabb90391028 ("fuse: increase readdir buffer size") changed fuse_readdir_uncached() to size its temporary buffer from ctx->count. This is useful for overlayfs and other in-kernel callers that use INT_MAX to indicate an unlimited directory read.
The larger buffer is currently supplied as a kvec output argument. For virtiofs, kvec arguments are copied through req->argbuf, which is allocated with kmalloc(..., GFP_ATOMIC). A large uncached readdir buffer can therefore require a multi-megabyte contiguous atomic allocation before the request is queued.
Avoid the large bounce-buffer allocation by backing uncached readdir output with pages and setting out_pages. Transports such as virtiofs can then pass the pages as scatter-gather entries instead of copying the output through argbuf.
Map the pages with vm_map_ram() only while parsing the returned dirents. The existing parser can then continue to use a linear kernel mapping.
[SzM: separate allocation of pages into a helper function]
--- fs/fuse/readdir.c | 85 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 18 deletions(-)
fs/fuse/readdir.c +67 -18
@@ -12,6 +12,7 @@#include <linux/posix_acl.h> #include <linux/pagemap.h> #include <linux/highmem.h> +#include <linux/vmalloc.h> static bool fuse_use_readdirplus (struct inode *dir,struct dir_context *ctx) {
@@ -335,6 +336,43 @@ static int parse_dirplusfile(char *buf,return 0 ; } +static struct page **fuse_readdir_alloc_buf (struct fuse_args_pages *ap,size_t *bufsize) +{ +unsigned int i, nr_alloc, nr_pages =DIV_ROUND_UP (*bufsize,PAGE_SIZE ); +struct page **pages =kcalloc (nr_pages,sizeof (*pages),GFP_KERNEL ); + +if (!pages) +return NULL ; + + nr_alloc =alloc_pages_bulk (GFP_KERNEL , nr_pages, pages); +if (!nr_alloc) + goto free_array; + +if (nr_alloc < nr_pages) { + nr_pages = nr_alloc; + *bufsize = (size_t ) nr_pages <<PAGE_SHIFT ; + } + + ap->folios =fuse_folios_alloc (nr_pages,GFP_KERNEL , &ap->descs); +if (!ap->folios) + goto release_pages; + +for (i =0 ; i < nr_pages; i++) { + ap->folios[i] =page_folio (pages[i]); + ap->descs[i].length =min_t (size_t, *bufsize - (size_t )i *PAGE_SIZE ,PAGE_SIZE ); + } + ap->num_folios = nr_pages; + ap->args.out_pages = true; + +return pages; + +release_pages: +release_pages (pages, nr_pages); +free_array: +kfree (pages); +return NULL ; +} +static int fuse_readdir_uncached (struct file *file,struct dir_context *ctx) {int plus;
@@ -343,18 +381,16 @@ static int fuse_readdir_uncached(structstruct fuse_mount *fm =get_fuse_mount (inode);struct fuse_conn *fc = fm->fc;struct fuse_io_args ia = {}; -struct fuse_args *args = &ia.ap.args; +struct fuse_args_pages *ap = &ia.ap;void *buf;size_t bufsize =clamp ((unsigned int ) ctx->count,PAGE_SIZE , fc->max_pages <<PAGE_SHIFT );u64 attr_version =0 , evict_ctr =0 ;bool locked; +struct page **pages =fuse_readdir_alloc_buf (ap, &bufsize); - buf =kvmalloc (bufsize,GFP_KERNEL ); -if (!buf) +if (!pages)return -ENOMEM ; - args->out_args[0 ].value = buf; - plus =fuse_use_readdirplus (inode, ctx);if (plus) { attr_version =fuse_get_attr_version (fm->fc);
@@ -364,24 +400,37 @@ static int fuse_readdir_uncached(structfuse_read_args_fill (&ia, file, ctx->pos, bufsize,FUSE_READDIR ); } locked =fuse_lock_inode (inode); - res =fuse_simple_request (fm, args); + res =fuse_simple_request (fm, &ap->args);fuse_unlock_inode (inode, locked); -if (res >=0 ) { -if (!res) { -struct fuse_file *ff = file->private_data; - -if (ff->open_flags &FOPEN_CACHE_DIR ) -fuse_readdir_cache_end (file, ctx->pos); - }else if (plus) { - res =parse_dirplusfile (buf, res, file, ctx, attr_version, - evict_ctr); - }else { +if (res <0 ) + goto out; + +if (!res) { +struct fuse_file *ff = file->private_data; + +if (ff->open_flags &FOPEN_CACHE_DIR ) +fuse_readdir_cache_end (file, ctx->pos); + goto out; + } + + buf =vm_map_ram (pages, ap->num_folios,-1 ); +if (!buf) { + res = -ENOMEM ; + }else { +if (plus) + res =parse_dirplusfile (buf, res, file, ctx, attr_version, evict_ctr); +else res =parse_dirfile (buf, res, file, ctx); - } + +vm_unmap_ram (buf, ap->num_folios); } +out: +kfree (ap->folios); +release_pages (pages, ap->num_folios); +kfree (pages); -kvfree (buf);fuse_invalidate_atime (inode); +return res; }
Greg Kroah-Hartman
maintainerPATCH 500/518 fuse: avoid 32-bit prune notification count wrap
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Samuel Moelius <[email protected]>
commit 54243797cedf55447b4c5d560e8cd709900061ae upstream.
FUSE_NOTIFY_PRUNE validates the nodeid payload length with:
size - sizeof(outarg) != outarg.count * sizeof(u64)
On 32-bit kernels, size_t is also 32 bits, so the daemon-controlled count multiplication can wrap. A prune notification with count 0x20000000 and no nodeid payload passes the check, enters the copy loop, and asks the device copy path to read nodeids that are not present in the userspace write buffer. In QEMU this reaches the fuse_copy_fill() BUG_ON(!err) path.
Validate the payload length with array_size() instead. That accepts exactly the same valid messages, but avoids wrapping arithmetic before the copy loop consumes the count.
--- fs/fuse/dev.c | 2 fs/fuse/notify.c | 434 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 fs/fuse/notify.c
fs/fuse/dev.c +1 -1
@@ -2072,7 +2072,7 @@ static int fuse_notify_prune(struct fuseif (err)return err; -if (size -sizeof (outarg) != outarg.count *sizeof (u64)) +if (size -sizeof (outarg) !=array_size (outarg.count,sizeof (u64)))return -EINVAL ;for (; outarg.count; outarg.count -= num) {
fs/fuse/notify.c new file +434 -0
434 lines not on this page · raw message
Greg Kroah-Hartman
maintainerPATCH 501/518 Revert "fuse: fix conversion of fuse_reverse_inval_entry() to start_removing()"
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miklos Szeredi <[email protected]>
commit c12bbaf7c2d19325913ffd7002e580e63952d9e2 upstream.
This reverts commit cab012375122304a6343c1ed09404e5143b9dc01.
Commit c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()") caused a regression in FUSE_NOTIFY_INVAL_ENTRY, which failed to invalidate negative dentries.
This manifests in the filesystem returning -ENOENT for operations on an existing file.
Fixing it properly while still keeping the start_removing* infrastructure would add much additional complexity.
Instead revert to the original simple implementation.
The start_removing* infrastructure is needed in VFS to abstract the filesystem locking. However filesystem code can still safely use the raw locking primitives without affacting other filesystems.
This is part one of the revert.
--- fs/fuse/dir.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)
fs/fuse/dir.c +7 -16
@@ -1587,8 +1587,8 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, {int err = -ENOTDIR ;struct inode *parent; -struct dentry *dir =NULL ; -struct dentry *entry =NULL ; +struct dentry *dir; +struct dentry *entry; parent =fuse_ilookup (fc, parent_nodeid,NULL );if (!parent)
@@ -1601,19 +1601,11 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, dir =d_find_alias (parent);if (!dir) goto put_parent; -while (!entry) { -struct dentry *child =try_lookup_noperm (name, dir); -if (!child ||IS_ERR (child)) - goto put_parent; - entry =start_removing_dentry (dir, child); -dput (child); -if (IS_ERR (entry)) - goto put_parent; -if (!d_same_name (entry, dir, name)) { -end_removing (entry); - entry =NULL ; - } - } + + entry =start_removing_noperm (dir, name); +dput (dir); +if (IS_ERR (entry)) + goto put_parent;fuse_dir_changed (parent);if (!(flags &FUSE_EXPIRE_ONLY ))
@@ -1651,7 +1643,6 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,end_removing (entry); put_parent: -dput (dir);iput (parent);return err; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 502/518 fuse: fix device node leak in cuse_process_init_reply()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alberto Ruiz <[email protected]>
commit 9fa4f7a53406430ee9982f2f636a15b338185122 upstream.
If device_add() succeeds during CUSE initialization but a subsequent step (cdev_alloc() or cdev_add()) fails, the error path calls put_device() without first calling device_del(). This leaks the devtmpfs entry created by device_add(), leaving a stale /dev/<name> node that persists until reboot.
Since the cuse_conn is never linked into cuse_conntbl on the failure path, cuse_channel_release() sees cc->dev == NULL and skips device_unregister(), so no other code path cleans up the node.
This has several consequences:
- The device name is permanently poisoned: any subsequent attempt to create a CUSE device with the same name hits the stale sysfs entry, device_add() fails, and the new device is aborted.
- The collision manifests as ENODEV returned to userspace with no dmesg diagnostic, making it very difficult to debug.
- The failure is self-perpetuating: once a name is leaked, all future attempts with that name fail identically.
Fix this by introducing an err_dev label that calls device_del() to undo device_add() before falling through to err_unlock. The existing err_unlock path from a device_add() failure correctly skips device_del() since the device was never added.
Testing instructions can be found at the lore link below.
--- fs/fuse/cuse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
fs/fuse/cuse.c +3 -1
@@ -391,7 +391,7 @@ static void cuse_process_init_reply(stru rc = -ENOMEM ; cdev =cdev_alloc ();if (!cdev) - goto err_unlock; + goto err_dev; cdev->owner =THIS_MODULE ; cdev->ops = &cuse_frontend_fops;
@@ -417,6 +417,8 @@ out: err_cdev:cdev_del (cdev); +err_dev: +device_del (dev); err_unlock:mutex_unlock (&cuse_lock);put_device (dev);
Greg Kroah-Hartman
maintainerPATCH 503/518 fuse: do not use start_removing_noperm()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Miklos Szeredi <[email protected]>
commit 6e1b235627bb1172d27c1b2ea7bf53e67dbced8d upstream.
Revert the fuse part of commit c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()").
Commit c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()") caused a regression in FUSE_NOTIFY_INVAL_ENTRY, which failed to invalidate negative dentries.
This manifests in the filesystem returning -ENOENT for operations on an existing file.
Fixing it properly while still keeping the start_removing* infrastructure would add much additional complexity.
Instead revert to the original simple implementation.
The start_removing* infrastructure is needed in VFS to abstract the filesystem locking. However filesystem code can still safely use the raw locking primitives without affacting other filesystems.
This is part two of the revert.
--- fs/fuse/dir.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
fs/fuse/dir.c +11 -8
@@ -1594,25 +1594,27 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,if (!parent)return -ENOENT ; +inode_lock_nested (parent,I_MUTEX_PARENT );if (!S_ISDIR (parent->i_mode)) - goto put_parent; + goto unlock; err = -ENOENT ; dir =d_find_alias (parent);if (!dir) - goto put_parent; + goto unlock; - entry =start_removing_noperm (dir, name); + name->hash =full_name_hash (dir, name->name, name->len); + entry =d_lookup (dir, name);dput (dir); -if (IS_ERR (entry)) - goto put_parent; +if (!entry) + goto unlock;fuse_dir_changed (parent);if (!(flags &FUSE_EXPIRE_ONLY ))d_invalidate (entry);fuse_invalidate_entry_cache (entry); -if (child_nodeid !=0 ) { +if (child_nodeid !=0 &&d_really_is_positive (entry)) {inode_lock (d_inode (entry));if (get_node_id (d_inode (entry)) != child_nodeid) { err = -ENOENT ;
@@ -1640,9 +1642,10 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, }else { err =0 ; } +dput (entry); -end_removing (entry); - put_parent: + unlock: +inode_unlock (parent);iput (parent);return err; }
Signature · 1 line
2.55.0
Greg Kroah-Hartman
maintainerPATCH 504/518 fuse: re-lock request before returning from fuse_ref_folio()
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
commit b5befa80fdbe287a98480effed9564712924add5 upstream.
fuse_ref_folio() unlocks the request but does not re-lock it before returning. fuse_chan_abort() can end the request and the async end callback (eg fuse_writepage_free()) can free the args while the subsequent copy chain logic after fuse_ref_folio() accesses them, leading to use-after-free issues.
Fix this by locking the request in fuse_ref_folio() before returning.
--- fs/fuse/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/fuse/dev.c +1 -1
@@ -1106,7 +1106,7 @@ static int fuse_ref_folio(struct fuse_co cs->nr_segs++; cs->len =0 ; -return 0 ; +return lock_request (cs->req); }/*
Greg Kroah-Hartman
maintainerPATCH 505/518 fuse: fix io-uring background queue dispatch on request completion
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
commit 31da059891bd3be9c6e59280b8e1777ead90db34 upstream.
When a background request completes via the io_uring path, the background queue gets flushed to dispatch pending background requests, but this is done before the connection-level background counters (fc->num_background, fc->active_background) are properly accounted, which may reduce effective queue depth to one.
The connection-level counters are decremented in fuse_request_end(), but flush_bg_queue() flushes the /dev/fuse path queue (fc->bg_queue), not the io_uring per-queue bg one, which means pending uring background requests on the queue are never dispatched in this path.
Fix this by accounting the connection-level background counters first before flushing the queue's background queue. Since fuse_request_bg_finish() clears FR_BACKGROUND, fuse_request_end() will skip the background cleanup branch entirely, which avoids any double-decrements; it will call the wake_up(&req->waitq) branch but this is effectively a no-op as background requests have no waiters on req->waitq.
--- fs/fuse/dev.c | 41 ++++++++++++++++++++++++----------------- fs/fuse/dev_uring.c | 1 + fs/fuse/fuse_dev_i.h | 1 + 3 files changed, 26 insertions(+), 17 deletions(-)
fs/fuse/dev.c +24 -17
@@ -447,6 +447,29 @@ static void flush_bg_queue(struct fuse_c } } +void fuse_request_bg_finish (struct fuse_conn *fc,struct fuse_req *req) +{ +lockdep_assert_held (&fc->bg_lock); + +clear_bit (FR_BACKGROUND , &req->flags); +if (fc->num_background == fc->max_background) { + fc->blocked =0 ; +wake_up (&fc->blocked_waitq); + }else if (!fc->blocked) { +/* +* Wake up next waiter, if any. It's okay to use +* waitqueue_active(), as we've already synced up +* fc->blocked with waiters with the wake_up() call +* above. +*/ +if (waitqueue_active (&fc->blocked_waitq)) +wake_up (&fc->blocked_waitq); + } + + fc->num_background--; + fc->active_background--; +} +/* * This function is called when a request is finished. Either a reply * has arrived or it was aborted (and not yet sent) or some error
@@ -479,23 +502,7 @@ void fuse_request_end(struct fuse_req *rWARN_ON (test_bit (FR_SENT , &req->flags));if (test_bit (FR_BACKGROUND , &req->flags)) {spin_lock (&fc->bg_lock); -clear_bit (FR_BACKGROUND , &req->flags); -if (fc->num_background == fc->max_background) { - fc->blocked =0 ; -wake_up (&fc->blocked_waitq); - }else if (!fc->blocked) { -/* -* Wake up next waiter, if any. It's okay to use -* waitqueue_active(), as we've already synced up -* fc->blocked with waiters with the wake_up() call -* above. -*/ -if (waitqueue_active (&fc->blocked_waitq)) -wake_up (&fc->blocked_waitq); - } - - fc->num_background--; - fc->active_background--; +fuse_request_bg_finish (fc, req);flush_bg_queue (fc);spin_unlock (&fc->bg_lock); }else {
fs/fuse/dev_uring.c +1 -0
@@ -90,6 +90,7 @@ static void fuse_uring_req_end(struct fuif (test_bit (FR_BACKGROUND , &req->flags)) { queue->active_background--;spin_lock (&fc->bg_lock); +fuse_request_bg_finish (fc, req);fuse_uring_flush_bg (queue);spin_unlock (&fc->bg_lock); }
fs/fuse/fuse_dev_i.h +1 -0
@@ -77,6 +77,7 @@ unsigned int fuse_req_hash(u64 unique);struct fuse_req *fuse_request_find (struct fuse_pqueue *fpq,u64 unique);void fuse_dev_end_requests (struct list_head *head); +void fuse_request_bg_finish (struct fuse_conn *fc,struct fuse_req *req);void fuse_copy_init (struct fuse_copy_state *cs,bool write,struct iov_iter *iter);
Greg Kroah-Hartman
maintainerPATCH 506/518 fuse: dont block in fuse_get_dev() for non-sync_init case
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
commit 9f6f44aa5a58aaae0838126dc09460c3e1f56ccb upstream.
Commit a8dd5f1b73bc ("fuse: create fuse_dev on /dev/fuse open instead of mount") changed behavior so that fuse_get_dev() now unconditionally blocks waiting for a connection, even in the case where sync_init was not set. Previously, non-sync_init opens returned -EPERM immediately.
Restore the previous behavior of returning -EPERM.
--- fs/fuse/dev.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
fs/fuse/dev.c +9 -3
@@ -1560,9 +1560,15 @@ struct fuse_dev *fuse_get_dev(struct filstruct fuse_dev *fud =fuse_file_to_fud (file);int err; - err =wait_event_interruptible (fuse_dev_waitq,fuse_dev_fc_get (fud) !=NULL ); -if (err) -return ERR_PTR (err); +if (unlikely (!fuse_dev_fc_get (fud))) { +/* only block waiting for mount if sync init was requested */ +if (!fud->sync_init) +return ERR_PTR (-EPERM ); + + err =wait_event_interruptible (fuse_dev_waitq,fuse_dev_fc_get (fud) !=NULL ); +if (err) +return ERR_PTR (err); + }return fud; }
Greg Kroah-Hartman
maintainerPATCH 507/518 fuse: clear intr_entry in fuse_resend and fuse_remove_pending_req
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ji'an Zhou <[email protected]>
commit f8fce75fedf73ac72aa09163deb8f4291fdcaad2 upstream.
When fuse_resend() moves a request from fpq->processing back to fiq->pending, it sets FR_PENDING and clears FR_SENT but does not remove the requests intr_entry from fiq->interrupts. If the request had FR_INTERRUPTED set from a prior signal, intr_entry remains dangling on fiq->interrupts. When the requesting task then receives a fatal signal, fuse_remove_pending_req() sees FR_PENDING=1, removes the request from fiq->pending and frees it via the refcount path, also without cleaning intr_entry. The stale intr_entry causes use-after-free when fuse_read_interrupt() iterates fiq->interrupts:
- list_del_init(&req->intr_entry) -> UAF write on freed slab
- req->in.h.unique -> UAF read, data leaked to userspace
Remove intr_entry from fiq->interrupts in fuse_resend() for interrupted requests before they are placed back on fiq->pending.
Add a WARN_ON if the intr_entry is not empty on request destruction.
--- fs/fuse/dev.c | 9 +++++++++ 1 file changed, 9 insertions(+)
fs/fuse/dev.c +9 -0
@@ -148,6 +148,7 @@ static struct fuse_req *fuse_request_allstatic void fuse_request_free (struct fuse_req *req) { +WARN_ON (!list_empty (&req->intr_entry));kmem_cache_free (fuse_req_cachep, req); }
@@ -2041,6 +2042,14 @@ static void fuse_resend(struct fuse_connfuse_dev_end_requests (&to_queue);return ; } +/* +* Remove interrupt entries for resent requests to prevent stale +* intr_entry on fiq->interrupts after the request is re-queued. +*/ +list_for_each_entry (req, &to_queue, list) { +if (test_bit (FR_INTERRUPTED , &req->flags)) +list_del_init (&req->intr_entry); + }/* iq and pq requests are both oldest to newest */ list_splice (&to_queue, &fiq->pending);fuse_dev_wake_and_unlock (fiq);
Greg Kroah-Hartman
maintainerPATCH 508/518 fuse-uring: fix EFAULT clobber in fuse_uring_commit
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 3a0a8bc51a13951c5141262bf770eeea3e0b6228 upstream.
copy_from_user() returns the number of bytes not copied as an unsigned residual on failure (1..sizeof(struct fuse_out_header)). fuse_uring_commit stores that residual in ssize_t err, sets req->out.h.error to -EFAULT, then jumps to out: with err still holding the positive residual.
err = copy_from_user(&req->out.h, &ent->headers->in_out,
sizeof(req->out.h));
if (err) {
req->out.h.error = -EFAULT;
goto out; /* err is the positive residual */
}
...
out:
fuse_uring_req_end(ent, req, err);fuse_uring_req_end() then runs
if (error)
req->out.h.error = error;which overwrites the just-assigned -EFAULT with the positive residual. FUSE callers such as fuse_simple_request() test err < 0 to detect failure, so the positive value is interpreted as success and the caller proceeds with an uninitialised or partial req->out.args.
Fix by assigning err = -EFAULT in the failure branch before jumping to out, so fuse_uring_req_end() receives a negative errno and sets req->out.h.error to -EFAULT.
--- fs/fuse/dev_uring.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-)
fs/fuse/dev_uring.c +3 -6
@@ -814,14 +814,11 @@ static void fuse_uring_commit(struct fus {struct fuse_ring *ring = ent->queue->ring;struct fuse_conn *fc = ring->fc; -ssize_t err =0 ; +ssize_t err = -EFAULT ; - err =copy_from_user (&req->out.h, &ent->headers->in_out, -sizeof (req->out.h)); -if (err) { - req->out.h.error = -EFAULT ; +if (copy_from_user (&req->out.h, &ent->headers->in_out, +sizeof (req->out.h))) goto out; - } err =fuse_uring_out_header_has_err (&req->out.h, req, fc);if (err) {
Greg Kroah-Hartman
maintainerPATCH 509/518 fuse-uring: fix data races on ring->ready
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit 46725a0056c884cf58a6897f222892807327d82d upstream.
On weakly-ordered architectures, the store to fiq->ops can be reordered past the store to ring->ready, allowing a CPU that sees ring->ready == true via fuse_uring_ready() to dispatch requests through a stale fiq->ops pointer. Upgrade the store to smp_store_release() and the load in fuse_uring_ready() to smp_load_acquire() so that the preceding WRITE_ONCE(fiq->ops, ...) is visible to any CPU that observes ring->ready == true.
Additionally, fuse_uring_do_register() publishes ring->ready with WRITE_ONCE() but the fast-path check reads it with a plain load. This is a marked-vs-unmarked access that KCSAN will flag. Wrap it in READ_ONCE() to mark it without adding unnecessary ordering.
Also wrap the fc->ring load in fuse_uring_ready() in READ_ONCE() to prevent the compiler from reloading it between the NULL check and the dereference.
--- fs/fuse/dev_uring.c | 4 ++-- fs/fuse/dev_uring_i.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-)
fs/fuse/dev_uring.c +2 -2
@@ -989,12 +989,12 @@ static void fuse_uring_do_register(strucfuse_uring_ent_avail (ent, queue);spin_unlock (&queue->lock); -if (!ring->ready) { +if (!READ_ONCE (ring->ready)) {bool ready =is_ring_ready (ring, queue->qid);if (ready) {WRITE_ONCE (fiq->ops, &fuse_io_uring_ops); -WRITE_ONCE (ring->ready, true); +smp_store_release (&ring->ready, true);wake_up_all (&fc->blocked_waitq); } }
fs/fuse/dev_uring_i.h +3 -1
@@ -169,7 +169,9 @@ static inline void fuse_uring_wait_stoppstatic inline bool fuse_uring_ready (struct fuse_conn *fc) { -return fc->ring && fc->ring->ready; +struct fuse_ring *ring =READ_ONCE (fc->ring); + +return ring &&smp_load_acquire (&ring->ready); }#else /* CONFIG_FUSE_IO_URING */
Greg Kroah-Hartman
maintainerPATCH 510/518 fuse-uring: fix moving cancelled entry to ent_in_userspace list
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
commit 198f45eeb9f78b2a2d6d8be95e4e43468eb2c6bc upstream.
fuse_uring_cancel() moves entries that are available (these have no reqs attached) to the ent_in_userspace list. ent_list_request_expired() checks the first entry on ent_in_userspace and dereferences ent->fuse_req unconditionally, which will crash on a cancelled entry that was moved to this list.
Fix this by freeing the entry and dropping queue_refs directly in fuse_uring_cancel(). This is safe because cancel is the cancel handler itself - after io_uring_cmd_done(), no more cancels will be dispatched for this command, and teardown serializes with cancel via queue->lock.
Since cancel now decrements queue_refs, fuse_uring_abort() must no longer gate fuse_uring_abort_end_requests() on queue_refs > 0, as cancelled entries may have already dropped queue_refs while requests are still queued. Remove the gate so abort always flushes requests and stops queues.
--- fs/fuse/dev_uring.c | 6 ++++-- fs/fuse/dev_uring_i.h | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-)
fs/fuse/dev_uring.c +4 -2
@@ -508,8 +508,7 @@ static void fuse_uring_cancel(struct io_ queue = ent->queue;spin_lock (&queue->lock);if (ent->state ==FRRS_AVAILABLE ) { - ent->state =FRRS_USERSPACE ; -list_move_tail (&ent->list, &queue->ent_in_userspace); +list_del_init (&ent->list); need_cmd_done = true; ent->cmd =NULL ; }
@@ -518,6 +517,9 @@ static void fuse_uring_cancel(struct io_if (need_cmd_done) {/* no queue lock to avoid lock order issues */ io_uring_cmd_done (cmd, -ENOTCONN , issue_flags); +kfree (ent); +if (atomic_dec_and_test (&queue->ring->queue_refs)) +wake_up_all (&queue->ring->stop_waitq); } }
fs/fuse/dev_uring_i.h +3 -3
@@ -152,10 +152,10 @@ static inline void fuse_uring_abort(struif (ring ==NULL )return ; -if (atomic_read (&ring->queue_refs) >0 ) { -fuse_uring_abort_end_requests (ring); +fuse_uring_abort_end_requests (ring); + +if (atomic_read (&ring->queue_refs) >0 )fuse_uring_stop_queues (ring); - } }static inline void fuse_uring_wait_stopped_queues (struct fuse_conn *fc)
Greg Kroah-Hartman
maintainerPATCH 511/518 fuse-uring: end fuse_req on io-uring cancel task work
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chris Mason <[email protected]>
commit bea4fe98204b6ce7eb8e29f7bf867dd7619b3ddd upstream.
When io_uring delivers task work with tw.cancel set (PF_EXITING, PF_KTHREAD fallback, or percpu_ref_is_dying on the ring context), fuse_uring_send_in_task() takes the cancel branch, assigns -ECANCELED, and falls through to fuse_uring_send(). That path only flips the entry to FRRS_USERSPACE and completes the io_uring cmd; it never discharges the ring entry's owning reference to the fuse_req that fuse_uring_add_req_to_ring_ent() handed it at dispatch time.
fuse_uring_send_in_task()
tw.cancel == true
err = -ECANCELED
fuse_uring_send(ent, cmd, err, issue_flags)
ent->state = FRRS_USERSPACE
list_move(&ent->list, &queue->ent_in_userspace)
ent->cmd = NULL
io_uring_cmd_done(-ECANCELED)
/* ent->fuse_req still set, req still hashed */The fuse_req stays linked on fpq->processing[hash] and fuse_request_end() is never invoked. The originating syscall thread blocks in D-state in request_wait_answer() until fuse_abort_conn() runs, which can be the entire connection lifetime. For FR_BACKGROUND requests fc->num_background is never decremented either, so repeated cancels inflate the counter until max_background is hit and all later background ops stall. tw.cancel does not imply a connection abort (e.g. a single io_uring worker thread exits while the fuse connection stays up), so this cannot be left for fuse_abort_conn() to clean up.
Ending the req but still routing the entry through fuse_uring_send() is not enough: that leaves a req-less entry on ent_in_userspace, and ent_list_request_expired() dereferences ent->fuse_req unconditionally on the head of that list, which would then NULL-deref.
Fix the cancel branch to release the entry directly. Remove it from the queue, complete the io_uring cmd, end the fuse_req, free the entry, and drop its queue_refs (waking the teardown waiter if it was the last).
--- fs/fuse/dev_uring.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
fs/fuse/dev_uring.c +12 -2
@@ -1225,11 +1225,21 @@ static void fuse_uring_send_in_task(strufuse_uring_next_fuse_req (ent, queue, issue_flags);return ; } +fuse_uring_send (ent, cmd, err, issue_flags); }else { err = -ECANCELED ; - } -fuse_uring_send (ent, cmd, err, issue_flags); +spin_lock (&queue->lock); +list_del_init (&ent->list); +spin_unlock (&queue->lock); + +io_uring_cmd_done (cmd, err, issue_flags); + +fuse_uring_req_end (ent, ent->fuse_req, err); +kfree (ent); +if (atomic_dec_and_test (&queue->ring->queue_refs)) +wake_up_all (&queue->ring->stop_waitq); + } }static struct fuse_ring_queue *fuse_uring_task_to_queue (struct fuse_ring *ring)
Greg Kroah-Hartman
maintainerPATCH 512/518 fuse-uring: Avoid use-after-free in fuse_uring_async_stop_queues
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernd Schubert <[email protected]>
commit d351da75066955144515cb2f9aa959f24a04287a upstream.
fuse_uring_async_stop_queues() might run when the last reference on ring->queue_refs was already dropped.
In order to avoid an early destruction a reference on struct fuse_conn is now taken before starting fuse_uring_async_stop_queues() and that reference is only released when that delayed work queue terminates.
--- fs/fuse/dev_uring.c | 2 ++ 1 file changed, 2 insertions(+)
fs/fuse/dev_uring.c +2 -0
@@ -467,6 +467,7 @@ static void fuse_uring_async_stop_queues FUSE_URING_TEARDOWN_INTERVAL); }else {wake_up_all (&ring->stop_waitq); +fuse_conn_put (ring->chan->conn); } }
@@ -478,6 +479,7 @@ void fuse_uring_stop_queues(struct fuse_fuse_uring_teardown_all_queues (ring);if (atomic_read (&ring->queue_refs) >0 ) { +fuse_conn_get (ring->chan->conn); ring->teardown_time = jiffies;INIT_DELAYED_WORK (&ring->async_teardown_work, fuse_uring_async_stop_queues);
Greg Kroah-Hartman
maintainerPATCH 513/518 fuse-uring: Avoid queue->stopped races and set/read that value under lock
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernd Schubert <[email protected]>
commit b70a3aca16934c196f92abb17b01c1647b9bb63c upstream.
There are several readers of queue->stopped that check the value under lock, but fuse_uring_commit_fetch() did not and actually the value was not set under the lock in fuse_uring_abort_end_requests() either. Especially in fuse_uring_commit_fetch it is important to check under a lock, because due to races 'struct fuse_req' might be freed with fuse_request_end, but another thread/cpu might already do teardown work.
--- fs/fuse/dev_uring.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
fs/fuse/dev_uring.c +9 -5
@@ -131,10 +131,9 @@ void fuse_uring_abort_end_requests(strucif (!queue)continue ; - queue->stopped = true; -WARN_ON_ONCE (ring->fc->max_background !=UINT_MAX );spin_lock (&queue->lock); + queue->stopped = true;spin_lock (&fc->bg_lock);fuse_uring_flush_bg (queue);spin_unlock (&fc->bg_lock);
@@ -467,7 +466,7 @@ static void fuse_uring_async_stop_queues FUSE_URING_TEARDOWN_INTERVAL); }else {wake_up_all (&ring->stop_waitq); -fuse_conn_put (ring->chan->conn); +fuse_conn_put (ring->fc); } }
@@ -479,7 +478,7 @@ void fuse_uring_stop_queues(struct fuse_fuse_uring_teardown_all_queues (ring);if (atomic_read (&ring->queue_refs) >0 ) { -fuse_conn_get (ring->chan->conn); +fuse_conn_get (ring->fc); ring->teardown_time = jiffies;INIT_DELAYED_WORK (&ring->async_teardown_work, fuse_uring_async_stop_queues);
@@ -900,10 +899,15 @@ static int fuse_uring_commit_fetch(strucreturn err; fpq = &queue->fpq; -if (!READ_ONCE (fc->connected) ||READ_ONCE (queue->stopped)) +if (!READ_ONCE (fc->connected))return err;spin_lock (&queue->lock); +if (unlikely (queue->stopped)) { +spin_unlock (&queue->lock); +return err; + } +/* Find a request based on the unique ID of the fuse request * This should get revised, as it needs a hash calculation and list * search. And full struct fuse_pqueue is needed (memory overhead).
Greg Kroah-Hartman
maintainerPATCH 514/518 fuse-uring: make a fuse_req on SQE commit only findable after memcpy
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bernd Schubert <[email protected]>
commit 1efd3d474fc0ba74dfd984249bca78807d739812 upstream.
Bad userspace might try to trick us and send commit SQEs request unique / commit-id of requests that are not even send to fuse-server (io_uring_cmd_done() not called) yet.
fuse_uring_commit_fetch() ends the fuse request when the ring entry has a wrong state, but that could have caused a use-after-free with the memcpy operations in fuse_uring_send_in_task(). In order to avoid such races the call of fuse_uring_add_to_pq() is moved after the copy operations and just before completing the io-uring request - malicious userspace cannot find the request anymore until all prepration work in fuse-client/kernel is completed.
This also moves fuse_uring_add_to_pq() a bit up in the code to avoid a forward declaration. Also not with a preparation commit, to make it easier to back port to older kernels.
--- fs/fuse/dev_uring.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-)
fs/fuse/dev_uring.c +19 -14
@@ -714,6 +714,19 @@ static int fuse_uring_prepare_send(strucreturn err; } +/* Used to find the request on SQE commit */ +static void fuse_uring_add_to_pq (struct fuse_ring_ent *ent) +{ +struct fuse_ring_queue *queue = ent->queue; +struct fuse_pqueue *fpq = &queue->fpq; +unsigned int hash; +struct fuse_req *req = ent->fuse_req; + + req->ring_entry = ent; + hash =fuse_req_hash (req->in.h.unique); +list_move_tail (&req->list, &fpq->processing[hash]); +} +/* * Write data to the ring buffer and send the request to userspace, * userspace will read it
@@ -736,6 +749,7 @@ static int fuse_uring_send_next_to_ring( ent->cmd =NULL ; ent->state =FRRS_USERSPACE ;list_move_tail (&ent->list, &queue->ent_in_userspace); +fuse_uring_add_to_pq (ent);spin_unlock (&queue->lock);io_uring_cmd_done (cmd,0 , issue_flags);
@@ -753,19 +767,6 @@ static void fuse_uring_ent_avail(struct ent->state =FRRS_AVAILABLE ; } -/* Used to find the request on SQE commit */ -static void fuse_uring_add_to_pq (struct fuse_ring_ent *ent, -struct fuse_req *req) -{ -struct fuse_ring_queue *queue = ent->queue; -struct fuse_pqueue *fpq = &queue->fpq; -unsigned int hash; - - req->ring_entry = ent; - hash =fuse_req_hash (req->in.h.unique); -list_move_tail (&req->list, &fpq->processing[hash]); -} -/* * Assign a fuse queue entry to the given entry */
@@ -783,10 +784,13 @@ static void fuse_uring_add_req_to_ring_e }clear_bit (FR_PENDING , &req->flags); + +/* Until fuse_uring_add_to_pq() the req is not attached to any list */ +list_del_init (&req->list); + ent->fuse_req = req; ent->state =FRRS_FUSE_REQ ;list_move_tail (&ent->list, &queue->ent_w_req_queue); -fuse_uring_add_to_pq (ent, req); }/* Fetch the next fuse request if available */
@@ -1207,6 +1211,7 @@ static void fuse_uring_send(struct fuse_ ent->state =FRRS_USERSPACE ;list_move_tail (&ent->list, &queue->ent_in_userspace); ent->cmd =NULL ; +fuse_uring_add_to_pq (ent);spin_unlock (&queue->lock);io_uring_cmd_done (cmd, ret, issue_flags);
Greg Kroah-Hartman
maintainerPATCH 515/518 fuse-uring: remove request-less entries from ent_w_req_queue to fix NULL deref
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Joanne Koong <[email protected]>
commit 1c57a69be962d459c5e705f5cb4355b841b3461c upstream.
If a copy into the userspace ring buffer fails, a request will be terminated and fuse_uring_req_end() will set ent->fuse_req to NULL but it will leave the entry on ent_w_req_queue in FRRS_FUSE_REQ state. This can lead to a NULL deref if the request expiration logic scans ent_w_req_queue in the window before the entry is moved off it.
Fix this by taking the entry off ent_w_req_queue and changing its state from FRRS_FUSE_REQ to FRRS_INVALID before terminating the request.
--- fs/fuse/dev_uring.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
fs/fuse/dev_uring.c +12 -2
@@ -706,10 +706,20 @@ static int fuse_uring_prepare_send(strucint err; err =fuse_uring_copy_to_ring (ent, req); -if (!err) +if (!err) {set_bit (FR_SENT , &req->flags); -else + }else { +/* +* Copying the request failed. Remove the entry from the +* ent_w_req_queue list and terminate the request +*/ +spin_lock (&ent->queue->lock); +list_del_init (&ent->list); + ent->state =FRRS_INVALID ; +spin_unlock (&ent->queue->lock); +fuse_uring_req_end (ent, req, err); + }return err; }
Greg Kroah-Hartman
maintainerPATCH 516/518 ALSA: doc: usb-audio: Add doc for QUIRK_FLAG_IFB_SILENCE_ON_EMPTY
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rong Zhang <[email protected]>
commit 06363f96e3d6b54ff7b5d2ce85cab95bd5e874b0 upstream.
QUIRK_FLAG_IFB_SILENCE_ON_EMPTY was introduced into usb-audio before without appropriate documentation, so add it.
--- Documentation/sound/alsa-configuration.rst | 6 ++++++ 1 file changed, 6 insertions(+)
Documentation/sound/alsa-configuration.rst +6 -0
@@ -2383,6 +2383,12 @@ quirk_flags ``V(x) = k * x``; ``dB(x) = 20 * log10(x)``. Overrides bit 24 * bit 28: ``mixer_capture_linear_vol`` Similar to bit 27 but for capture streams. Overrides bit 25 + * bit 29: ``ifb_silence_on_empty`` + In implicit feedback mode, when an entire capture URB returns with + all iso_frame_desc[i].status != 0 (bytes==0), do not silently return + from snd_usb_handle_sync_urb. Instead fall through and enqueue a + packet_info containing only size-0 packets, so the OUT ring keeps + moving (emits silence). Needed by Behringer Flow 8 (1397:050c). This module supports multiple devices, autoprobe and hotplugging.
Greg Kroah-Hartman
maintainerPATCH 517/518 timekeeping: Register default clocksource before taking tk_core.lock
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mikhail Gavrilov <[email protected]>
commit 8fa30821180a9a19e78e9f4df1c0ba710252801e upstream.
Commit f24df84cbe05 ("time/jiffies: Register jiffies clocksource before usage") moved the jiffies clocksource registration into clocksource_default_clock(), so that it is registered lazily on the first call. __clocksource_register() acquires clocksource_mutex, but the first caller is timekeeping_init(), which invokes clocksource_default_clock() while holding tk_core.lock, a raw spinlock.
Acquiring a sleeping mutex while holding a raw spinlock is invalid.
The default clocksource only has to be registered before tk_setup_internals() consumes its mult/shift/maxadj. Neither clocksource_default_clock(), the ->enable() callback, nor the registration itself need tk_core.lock, so fetch and enable the clock before acquiring the lock. This preserves the "register before usage" ordering while keeping clocksource_mutex out of the raw spinlock section.
clocksource_default_clock() has a second caller, clocksource_done_booting(), which invokes it with clocksource_mutex already held. That path avoids a recursive lock because timekeeping_init() has already run and set cs_jiffies_registered, so the registration is skipped there. This change does not alter that; it only fixes the invalid wait context in timekeeping_init().
--- kernel/time/timekeeping.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
kernel/time/timekeeping.c +4 -3
@@ -1980,13 +1980,14 @@ void __init timekeeping_init(void)*/ wall_to_mono =timespec64_sub (boot_offset, wall_time); + clock =clocksource_default_clock (); +if (clock->enable) + clock->enable (clock); +guard (raw_spinlock_irqsave)(&tk_core.lock);ntp_init (); - clock =clocksource_default_clock (); -if (clock->enable) - clock->enable (clock);tk_setup_internals (tks, clock);tk_set_xtime (tks, &wall_time);
Greg Kroah-Hartman
maintainerPATCH 518/518 Bluetooth: 6lowpan: Fix using chan->conn as indication to no remote netdev
In reply to Greg Kroah-Hartman7.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Luiz Augusto von Dentz <[email protected]>
commit d38eaf611839b85ade3dd3db309dbc8aaaaf0095 upstream.
b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref") don't reset the chan->conn to NULL anymore making the bt# netdev not be remove once the last l2cap_chan_del is removed.
Instead of restoring the original behavior this remove the logic of keeping the interface after the last channel is removed because it never worked as intended and the l2cap_chan_del always detach its l2cap_conn which results in always removing the channel anyway.
--- net/bluetooth/6lowpan.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-)
net/bluetooth/6lowpan.c +3 -15
@@ -777,20 +777,10 @@ static void chan_close_cb(struct l2cap_cstruct lowpan_btle_dev *dev =NULL ;struct lowpan_peer *peer;int err = -ENOENT ; -bool last = false, remove = true; +bool last = false;BT_DBG ("chan %p conn %p" , chan, chan->conn); -if (chan->conn && chan->conn->hcon) { -if (!is_bt_6lowpan (chan->conn->hcon)) -return ; - -/* If conn is set, then the netdev is also there and we should -* not remove it. -*/ - remove = false; - } -spin_lock (&devices_lock);list_for_each_entry_rcu (entry, &bt_6lowpan_devices, list) {
@@ -817,10 +807,8 @@ static void chan_close_cb(struct l2cap_cifdown (dev->netdev); -if (remove) { -INIT_WORK (&entry->delete_netdev, delete_netdev); -schedule_work (&entry->delete_netdev); - } +INIT_WORK (&entry->delete_netdev, delete_netdev); +schedule_work (&entry->delete_netdev); }else {spin_unlock (&devices_lock); }
Hi
kernel build / boot test on x86_64 (Intel).
No regressions here.
Thanks
# Librecast Test Results
020/020 [ OK ] liblcrq
010/010 [ OK ] libmld
120/120 [ OK ] liblibrecast
CPU/kernel: Linux auntie 7.1.4-rc1-g01c8c5ba0d4c #1 SMP PREEMPT_DYNAMIC Thu Jul 16 14:50:32 -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 7.1.4 release. There are 518 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 Sat, 18 Jul 2026 13:30:01 +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/v7.x/stable-review/patch-7.1.4-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.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
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.4 release. There are 518 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 Sat, 18 Jul 2026 13:30:01 +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/v7.x/stable-review/patch-7.1.4-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Tested rc1 against the Fedora build system (aarch64, ppc64le, s390x, x86_64), and boot tested x86_64. No regressions noted.
Am 16.07.2026 um 15:24 schrieb Greg Kroah-Hartman:
Quoted 4 lines
This is the start of the stable review cycle for the 7.1.4 release. There are 518 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]
Quoted 8 lines · On Thu, 16 Jul 2026 15:24:28 +0200 Greg Kroah-Hartman…
This is the start of the stable review cycle for the 7.1.4 release. There are 518 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 Sat, 18 Jul 2026 13:30:01 +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 and arm32:
Thanks!
Cheers,
Miguel
Hi Greg
Quoted 10 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.4 release. There are 518 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 Sat, 18 Jul 2026 13:30:01 +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/v7.x/stable-review/patch-7.1.4-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Linux version 7.1.4-rc1 tested.
Build successfully completed.
Boot successfully completed.
No dmesg regressions.
Video output normal.
Sound output normal.
Lenovo ThinkPad X1 Carbon Gen10(Intel i7-1260P(x86_64) arch linux)
[ 0.000000] Linux version 7.1.4-rc1rv-g01c8c5ba0d4c
(takeshi@ThinkPadX1Gen10J0764) (gcc (GCC) 16.1.1 20260625, GNU ld (GNU Binutils) 2.46.1) #1 SMP PREEMPT_DYNAMIC Fri Jul 17 17:03:38 JST 2026
Thanks
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.4 release. There are 518 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 Sat, 18 Jul 2026 13:30:01 +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/v7.x/stable-review/patch-7.1.4-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Built and booted successfully on RISC-V RV64 (HiFive Unmatched).
Quoted 9 lines · Greg Kroah-Hartman
This is the start of the stable review cycle for the 7.1.4 release. There are 518 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 Sat, 18 Jul 2026 13:30:01 +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/v7.x/stable-review/patch-7.1.4-rc1.gzor in the git tree and branch at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.gitlinux-7.1.y
and the diffstat can be found below.thanks,
greg k-h
Compiled and booted on my test system. No dmesg regressions.
thanks,
-- Shuah
Hi!
Quoted 4 lines
This is the start of the stable review cycle for the 7.1.4 release. There are 518 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-7.1.y
Best regards,
Pavel