From: Greg Kroah-Hartman Date: Wed, 19 Feb 2025 07:39:02 +0000 (+0100) Subject: 5.10-stable patches X-Git-Tag: v6.1.129~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bacf9c2332f023a1b3a480de1355b3ea7bff35a8;p=thirdparty%2Fkernel%2Fstable-queue.git 5.10-stable patches added patches: btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch ima-fix-use-after-free-on-a-dentry-s-dname.name.patch mm-call-the-security_mmap_file-lsm-hook-in-remap_file_pages.patch nvme-pci-fix-multiple-races-in-nvme_setup_io_queues.patch pps-fix-a-use-after-free.patch revert-btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch vlan-introduce-vlan_dev_free_egress_priority.patch vlan-move-dev_put-into-vlan_dev_uninit.patch --- diff --git a/queue-5.10/btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch b/queue-5.10/btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch new file mode 100644 index 0000000000..51b7d582ff --- /dev/null +++ b/queue-5.10/btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch @@ -0,0 +1,42 @@ +From stable+bounces-114128-greg=kroah.com@vger.kernel.org Thu Feb 6 17:20:42 2025 +From: Koichiro Den +Date: Fri, 7 Feb 2025 01:19:55 +0900 +Subject: btrfs: avoid monopolizing a core when activating a swap file +To: gregkh@linuxfoundation.org, stable@vger.kernel.org +Cc: wqu@suse.com, fdmanana@suse.com, dsterba@suse.com +Message-ID: <20250206161955.1387041-2-koichiro.den@canonical.com> + +From: Filipe Manana + +commit 2c8507c63f5498d4ee4af404a8e44ceae4345056 upstream. + +This commit re-attempts the backport of the change to the linux-5.10.y +branch. Commit a1c3a19446a4 ("btrfs: avoid monopolizing a core when +activating a swap file") on this branch was reverted. + +During swap activation we iterate over the extents of a file and we can +have many thousands of them, so we can end up in a busy loop monopolizing +a core. Avoid this by doing a voluntary reschedule after processing each +extent. + +CC: stable@vger.kernel.org # 5.4+ +Reviewed-by: Qu Wenruo +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Koichiro Den +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/inode.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -10422,6 +10422,8 @@ static int btrfs_swap_activate(struct sw + } + + start += len; ++ ++ cond_resched(); + } + + if (bsi.block_len) diff --git a/queue-5.10/ima-fix-use-after-free-on-a-dentry-s-dname.name.patch b/queue-5.10/ima-fix-use-after-free-on-a-dentry-s-dname.name.patch new file mode 100644 index 0000000000..44d5aa8719 --- /dev/null +++ b/queue-5.10/ima-fix-use-after-free-on-a-dentry-s-dname.name.patch @@ -0,0 +1,115 @@ +From be84f32bb2c981ca670922e047cdde1488b233de Mon Sep 17 00:00:00 2001 +From: Stefan Berger +Date: Fri, 22 Mar 2024 10:03:12 -0400 +Subject: ima: Fix use-after-free on a dentry's dname.name + +From: Stefan Berger + +commit be84f32bb2c981ca670922e047cdde1488b233de upstream. + +->d_name.name can change on rename and the earlier value can be freed; +there are conditions sufficient to stabilize it (->d_lock on dentry, +->d_lock on its parent, ->i_rwsem exclusive on the parent's inode, +rename_lock), but none of those are met at any of the sites. Take a stable +snapshot of the name instead. + +Link: https://lore.kernel.org/all/20240202182732.GE2087318@ZenIV/ +Signed-off-by: Al Viro +Signed-off-by: Stefan Berger +Signed-off-by: Mimi Zohar +[ Samasth: bp to fix CVE-2024-39494; Minor conflict resolved due to code context change ] +Signed-off-by: Samasth Norway Ananda +Signed-off-by: Greg Kroah-Hartman +--- + security/integrity/ima/ima_api.c | 16 ++++++++++++---- + security/integrity/ima/ima_template_lib.c | 17 ++++++++++++++--- + 2 files changed, 26 insertions(+), 7 deletions(-) + +--- a/security/integrity/ima/ima_api.c ++++ b/security/integrity/ima/ima_api.c +@@ -213,7 +213,7 @@ int ima_collect_measurement(struct integ + const char *audit_cause = "failed"; + struct inode *inode = file_inode(file); + struct inode *real_inode = d_real_inode(file_dentry(file)); +- const char *filename = file->f_path.dentry->d_name.name; ++ struct name_snapshot filename; + int result = 0; + int length; + void *tmpbuf; +@@ -276,9 +276,13 @@ out: + if (file->f_flags & O_DIRECT) + audit_cause = "failed(directio)"; + ++ take_dentry_name_snapshot(&filename, file->f_path.dentry); ++ + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, +- filename, "collect_data", audit_cause, +- result, 0); ++ filename.name.name, "collect_data", ++ audit_cause, result, 0); ++ ++ release_dentry_name_snapshot(&filename); + } + return result; + } +@@ -391,6 +395,7 @@ out: + */ + const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf) + { ++ struct name_snapshot filename; + char *pathname = NULL; + + *pathbuf = __getname(); +@@ -404,7 +409,10 @@ const char *ima_d_path(const struct path + } + + if (!pathname) { +- strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX); ++ take_dentry_name_snapshot(&filename, path->dentry); ++ strscpy(namebuf, filename.name.name, NAME_MAX); ++ release_dentry_name_snapshot(&filename); ++ + pathname = namebuf; + } + +--- a/security/integrity/ima/ima_template_lib.c ++++ b/security/integrity/ima/ima_template_lib.c +@@ -385,7 +385,10 @@ static int ima_eventname_init_common(str + bool size_limit) + { + const char *cur_filename = NULL; ++ struct name_snapshot filename; + u32 cur_filename_len = 0; ++ bool snapshot = false; ++ int ret; + + BUG_ON(event_data->filename == NULL && event_data->file == NULL); + +@@ -398,7 +401,10 @@ static int ima_eventname_init_common(str + } + + if (event_data->file) { +- cur_filename = event_data->file->f_path.dentry->d_name.name; ++ take_dentry_name_snapshot(&filename, ++ event_data->file->f_path.dentry); ++ snapshot = true; ++ cur_filename = filename.name.name; + cur_filename_len = strlen(cur_filename); + } else + /* +@@ -407,8 +413,13 @@ static int ima_eventname_init_common(str + */ + cur_filename_len = IMA_EVENT_NAME_LEN_MAX; + out: +- return ima_write_template_field_data(cur_filename, cur_filename_len, +- DATA_FMT_STRING, field_data); ++ ret = ima_write_template_field_data(cur_filename, cur_filename_len, ++ DATA_FMT_STRING, field_data); ++ ++ if (snapshot) ++ release_dentry_name_snapshot(&filename); ++ ++ return ret; + } + + /* diff --git a/queue-5.10/mm-call-the-security_mmap_file-lsm-hook-in-remap_file_pages.patch b/queue-5.10/mm-call-the-security_mmap_file-lsm-hook-in-remap_file_pages.patch new file mode 100644 index 0000000000..f9e785baeb --- /dev/null +++ b/queue-5.10/mm-call-the-security_mmap_file-lsm-hook-in-remap_file_pages.patch @@ -0,0 +1,78 @@ +From ea7e2d5e49c05e5db1922387b09ca74aa40f46e2 Mon Sep 17 00:00:00 2001 +From: Shu Han +Date: Tue, 17 Sep 2024 17:41:04 +0800 +Subject: mm: call the security_mmap_file() LSM hook in remap_file_pages() + +From: Shu Han + +commit ea7e2d5e49c05e5db1922387b09ca74aa40f46e2 upstream. + +The remap_file_pages syscall handler calls do_mmap() directly, which +doesn't contain the LSM security check. And if the process has called +personality(READ_IMPLIES_EXEC) before and remap_file_pages() is called for +RW pages, this will actually result in remapping the pages to RWX, +bypassing a W^X policy enforced by SELinux. + +So we should check prot by security_mmap_file LSM hook in the +remap_file_pages syscall handler before do_mmap() is called. Otherwise, it +potentially permits an attacker to bypass a W^X policy enforced by +SELinux. + +The bypass is similar to CVE-2016-10044, which bypass the same thing via +AIO and can be found in [1]. + +The PoC: + +$ cat > test.c + +int main(void) { + size_t pagesz = sysconf(_SC_PAGE_SIZE); + int mfd = syscall(SYS_memfd_create, "test", 0); + const char *buf = mmap(NULL, 4 * pagesz, PROT_READ | PROT_WRITE, + MAP_SHARED, mfd, 0); + unsigned int old = syscall(SYS_personality, 0xffffffff); + syscall(SYS_personality, READ_IMPLIES_EXEC | old); + syscall(SYS_remap_file_pages, buf, pagesz, 0, 2, 0); + syscall(SYS_personality, old); + // show the RWX page exists even if W^X policy is enforced + int fd = open("/proc/self/maps", O_RDONLY); + unsigned char buf2[1024]; + while (1) { + int ret = read(fd, buf2, 1024); + if (ret <= 0) break; + write(1, buf2, ret); + } + close(fd); +} + +$ gcc test.c -o test +$ ./test | grep rwx +7f1836c34000-7f1836c35000 rwxs 00002000 00:01 2050 /memfd:test (deleted) + +Link: https://project-zero.issues.chromium.org/issues/42452389 [1] +Cc: stable@vger.kernel.org +Signed-off-by: Shu Han +Acked-by: Stephen Smalley +[PM: subject line tweaks] +Signed-off-by: Paul Moore +Signed-off-by: Pratyush Yadav +Signed-off-by: Greg Kroah-Hartman +--- + mm/mmap.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -3078,8 +3078,12 @@ SYSCALL_DEFINE5(remap_file_pages, unsign + } + + file = get_file(vma->vm_file); ++ ret = security_mmap_file(vma->vm_file, prot, flags); ++ if (ret) ++ goto out_fput; + ret = do_mmap(vma->vm_file, start, size, + prot, flags, pgoff, &populate, NULL); ++out_fput: + fput(file); + out: + mmap_write_unlock(mm); diff --git a/queue-5.10/nvme-pci-fix-multiple-races-in-nvme_setup_io_queues.patch b/queue-5.10/nvme-pci-fix-multiple-races-in-nvme_setup_io_queues.patch new file mode 100644 index 0000000000..8063eebdfd --- /dev/null +++ b/queue-5.10/nvme-pci-fix-multiple-races-in-nvme_setup_io_queues.patch @@ -0,0 +1,222 @@ +From e4b9852a0f4afe40604afb442e3af4452722050a Mon Sep 17 00:00:00 2001 +From: Casey Chen +Date: Wed, 7 Jul 2021 14:14:31 -0700 +Subject: nvme-pci: fix multiple races in nvme_setup_io_queues + +From: Casey Chen + +commit e4b9852a0f4afe40604afb442e3af4452722050a upstream. + +Below two paths could overlap each other if we power off a drive quickly +after powering it on. There are multiple races in nvme_setup_io_queues() +because of shutdown_lock missing and improper use of NVMEQ_ENABLED bit. + +nvme_reset_work() nvme_remove() + nvme_setup_io_queues() nvme_dev_disable() + ... ... +A1 clear NVMEQ_ENABLED bit for admin queue lock + retry: B1 nvme_suspend_io_queues() +A2 pci_free_irq() admin queue B2 nvme_suspend_queue() admin queue +A3 pci_free_irq_vectors() nvme_pci_disable() +A4 nvme_setup_irqs(); B3 pci_free_irq_vectors() + ... unlock +A5 queue_request_irq() for admin queue + set NVMEQ_ENABLED bit + ... + nvme_create_io_queues() +A6 result = queue_request_irq(); + set NVMEQ_ENABLED bit + ... + fail to allocate enough IO queues: +A7 nvme_suspend_io_queues() + goto retry + +If B3 runs in between A1 and A2, it will crash if irqaction haven't +been freed by A2. B2 is supposed to free admin queue IRQ but it simply +can't fulfill the job as A1 has cleared NVMEQ_ENABLED bit. + +Fix: combine A1 A2 so IRQ get freed as soon as the NVMEQ_ENABLED bit +gets cleared. + +After solved #1, A2 could race with B3 if A2 is freeing IRQ while B3 +is checking irqaction. A3 also could race with B2 if B2 is freeing +IRQ while A3 is checking irqaction. + +Fix: A2 and A3 take lock for mutual exclusion. + +A3 could race with B3 since they could run free_msi_irqs() in parallel. + +Fix: A3 takes lock for mutual exclusion. + +A4 could fail to allocate all needed IRQ vectors if A3 and A4 are +interrupted by B3. + +Fix: A4 takes lock for mutual exclusion. + +If A5/A6 happened after B2/B1, B3 will crash since irqaction is not NULL. +They are just allocated by A5/A6. + +Fix: Lock queue_request_irq() and setting of NVMEQ_ENABLED bit. + +A7 could get chance to pci_free_irq() for certain IO queue while B3 is +checking irqaction. + +Fix: A7 takes lock. + +nvme_dev->online_queues need to be protected by shutdown_lock. Since it +is not atomic, both paths could modify it using its own copy. + +Co-developed-by: Yuanyuan Zhong +Signed-off-by: Casey Chen +Reviewed-by: Keith Busch +Signed-off-by: Christoph Hellwig +[noahm@debian.org: backported to 5.10] +Link: https://lore.kernel.org/linux-nvme/20210707211432.29536-1-cachen@purestorage.com/ +Signed-off-by: Noah Meyerhans +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvme/host/pci.c | 66 ++++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 58 insertions(+), 8 deletions(-) + +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -1563,6 +1563,28 @@ static void nvme_init_queue(struct nvme_ + wmb(); /* ensure the first interrupt sees the initialization */ + } + ++/* ++ * Try getting shutdown_lock while setting up IO queues. ++ */ ++static int nvme_setup_io_queues_trylock(struct nvme_dev *dev) ++{ ++ /* ++ * Give up if the lock is being held by nvme_dev_disable. ++ */ ++ if (!mutex_trylock(&dev->shutdown_lock)) ++ return -ENODEV; ++ ++ /* ++ * Controller is in wrong state, fail early. ++ */ ++ if (dev->ctrl.state != NVME_CTRL_CONNECTING) { ++ mutex_unlock(&dev->shutdown_lock); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ + static int nvme_create_queue(struct nvme_queue *nvmeq, int qid, bool polled) + { + struct nvme_dev *dev = nvmeq->dev; +@@ -1591,8 +1613,11 @@ static int nvme_create_queue(struct nvme + goto release_cq; + + nvmeq->cq_vector = vector; +- nvme_init_queue(nvmeq, qid); + ++ result = nvme_setup_io_queues_trylock(dev); ++ if (result) ++ return result; ++ nvme_init_queue(nvmeq, qid); + if (!polled) { + result = queue_request_irq(nvmeq); + if (result < 0) +@@ -1600,10 +1625,12 @@ static int nvme_create_queue(struct nvme + } + + set_bit(NVMEQ_ENABLED, &nvmeq->flags); ++ mutex_unlock(&dev->shutdown_lock); + return result; + + release_sq: + dev->online_queues--; ++ mutex_unlock(&dev->shutdown_lock); + adapter_delete_sq(dev, qid); + release_cq: + adapter_delete_cq(dev, qid); +@@ -2182,7 +2209,18 @@ static int nvme_setup_io_queues(struct n + if (nr_io_queues == 0) + return 0; + +- clear_bit(NVMEQ_ENABLED, &adminq->flags); ++ /* ++ * Free IRQ resources as soon as NVMEQ_ENABLED bit transitions ++ * from set to unset. If there is a window to it is truely freed, ++ * pci_free_irq_vectors() jumping into this window will crash. ++ * And take lock to avoid racing with pci_free_irq_vectors() in ++ * nvme_dev_disable() path. ++ */ ++ result = nvme_setup_io_queues_trylock(dev); ++ if (result) ++ return result; ++ if (test_and_clear_bit(NVMEQ_ENABLED, &adminq->flags)) ++ pci_free_irq(pdev, 0, adminq); + + if (dev->cmb_use_sqes) { + result = nvme_cmb_qdepth(dev, nr_io_queues, +@@ -2198,14 +2236,17 @@ static int nvme_setup_io_queues(struct n + result = nvme_remap_bar(dev, size); + if (!result) + break; +- if (!--nr_io_queues) +- return -ENOMEM; ++ if (!--nr_io_queues) { ++ result = -ENOMEM; ++ goto out_unlock; ++ } + } while (1); + adminq->q_db = dev->dbs; + + retry: + /* Deregister the admin queue's interrupt */ +- pci_free_irq(pdev, 0, adminq); ++ if (test_and_clear_bit(NVMEQ_ENABLED, &adminq->flags)) ++ pci_free_irq(pdev, 0, adminq); + + /* + * If we enable msix early due to not intx, disable it again before +@@ -2214,8 +2255,10 @@ static int nvme_setup_io_queues(struct n + pci_free_irq_vectors(pdev); + + result = nvme_setup_irqs(dev, nr_io_queues); +- if (result <= 0) +- return -EIO; ++ if (result <= 0) { ++ result = -EIO; ++ goto out_unlock; ++ } + + dev->num_vecs = result; + result = max(result - 1, 1); +@@ -2229,8 +2272,9 @@ static int nvme_setup_io_queues(struct n + */ + result = queue_request_irq(adminq); + if (result) +- return result; ++ goto out_unlock; + set_bit(NVMEQ_ENABLED, &adminq->flags); ++ mutex_unlock(&dev->shutdown_lock); + + result = nvme_create_io_queues(dev); + if (result || dev->online_queues < 2) +@@ -2239,6 +2283,9 @@ static int nvme_setup_io_queues(struct n + if (dev->online_queues - 1 < dev->max_qid) { + nr_io_queues = dev->online_queues - 1; + nvme_disable_io_queues(dev); ++ result = nvme_setup_io_queues_trylock(dev); ++ if (result) ++ return result; + nvme_suspend_io_queues(dev); + goto retry; + } +@@ -2247,6 +2294,9 @@ static int nvme_setup_io_queues(struct n + dev->io_queues[HCTX_TYPE_READ], + dev->io_queues[HCTX_TYPE_POLL]); + return 0; ++out_unlock: ++ mutex_unlock(&dev->shutdown_lock); ++ return result; + } + + static void nvme_del_queue_end(struct request *req, blk_status_t error) diff --git a/queue-5.10/pps-fix-a-use-after-free.patch b/queue-5.10/pps-fix-a-use-after-free.patch new file mode 100644 index 0000000000..6f9cf503b7 --- /dev/null +++ b/queue-5.10/pps-fix-a-use-after-free.patch @@ -0,0 +1,587 @@ +From c79a39dc8d060b9e64e8b0fa9d245d44befeefbe Mon Sep 17 00:00:00 2001 +From: Calvin Owens +Date: Mon, 11 Nov 2024 20:13:29 -0800 +Subject: pps: Fix a use-after-free + +From: Calvin Owens + +commit c79a39dc8d060b9e64e8b0fa9d245d44befeefbe upstream. + +On a board running ntpd and gpsd, I'm seeing a consistent use-after-free +in sys_exit() from gpsd when rebooting: + + pps pps1: removed + ------------[ cut here ]------------ + kobject: '(null)' (00000000db4bec24): is not initialized, yet kobject_put() is being called. + WARNING: CPU: 2 PID: 440 at lib/kobject.c:734 kobject_put+0x120/0x150 + CPU: 2 UID: 299 PID: 440 Comm: gpsd Not tainted 6.11.0-rc6-00308-gb31c44928842 #1 + Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT) + pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) + pc : kobject_put+0x120/0x150 + lr : kobject_put+0x120/0x150 + sp : ffffffc0803d3ae0 + x29: ffffffc0803d3ae0 x28: ffffff8042dc9738 x27: 0000000000000001 + x26: 0000000000000000 x25: ffffff8042dc9040 x24: ffffff8042dc9440 + x23: ffffff80402a4620 x22: ffffff8042ef4bd0 x21: ffffff80405cb600 + x20: 000000000008001b x19: ffffff8040b3b6e0 x18: 0000000000000000 + x17: 0000000000000000 x16: 0000000000000000 x15: 696e6920746f6e20 + x14: 7369203a29343263 x13: 205d303434542020 x12: 0000000000000000 + x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000 + x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000000000 + x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000 + x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000000 + Call trace: + kobject_put+0x120/0x150 + cdev_put+0x20/0x3c + __fput+0x2c4/0x2d8 + ____fput+0x1c/0x38 + task_work_run+0x70/0xfc + do_exit+0x2a0/0x924 + do_group_exit+0x34/0x90 + get_signal+0x7fc/0x8c0 + do_signal+0x128/0x13b4 + do_notify_resume+0xdc/0x160 + el0_svc+0xd4/0xf8 + el0t_64_sync_handler+0x140/0x14c + el0t_64_sync+0x190/0x194 + ---[ end trace 0000000000000000 ]--- + +...followed by more symptoms of corruption, with similar stacks: + + refcount_t: underflow; use-after-free. + kernel BUG at lib/list_debug.c:62! + Kernel panic - not syncing: Oops - BUG: Fatal exception + +This happens because pps_device_destruct() frees the pps_device with the +embedded cdev immediately after calling cdev_del(), but, as the comment +above cdev_del() notes, fops for previously opened cdevs are still +callable even after cdev_del() returns. I think this bug has always +been there: I can't explain why it suddenly started happening every time +I reboot this particular board. + +In commit d953e0e837e6 ("pps: Fix a use-after free bug when +unregistering a source."), George Spelvin suggested removing the +embedded cdev. That seems like the simplest way to fix this, so I've +implemented his suggestion, using __register_chrdev() with pps_idr +becoming the source of truth for which minor corresponds to which +device. + +But now that pps_idr defines userspace visibility instead of cdev_add(), +we need to be sure the pps->dev refcount can't reach zero while +userspace can still find it again. So, the idr_remove() call moves to +pps_unregister_cdev(), and pps_idr now holds a reference to pps->dev. + + pps_core: source serial1 got cdev (251:1) + <...> + pps pps1: removed + pps_core: unregistering pps1 + pps_core: deallocating pps1 + +Fixes: d953e0e837e6 ("pps: Fix a use-after free bug when unregistering a source.") +Cc: stable@vger.kernel.org +Signed-off-by: Calvin Owens +Reviewed-by: Michal Schmidt +Link: https://lore.kernel.org/r/a17975fd5ae99385791929e563f72564edbcf28f.1731383727.git.calvin@wbinvd.org +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pps/clients/pps-gpio.c | 4 - + drivers/pps/clients/pps-ktimer.c | 4 - + drivers/pps/clients/pps-ldisc.c | 6 - + drivers/pps/clients/pps_parport.c | 4 - + drivers/pps/kapi.c | 10 +- + drivers/pps/kc.c | 10 +- + drivers/pps/pps.c | 127 +++++++++++++++++++------------------- + include/linux/pps_kernel.h | 3 + 8 files changed, 86 insertions(+), 82 deletions(-) + +--- a/drivers/pps/clients/pps-gpio.c ++++ b/drivers/pps/clients/pps-gpio.c +@@ -232,8 +232,8 @@ static int pps_gpio_probe(struct platfor + return -EINVAL; + } + +- dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", +- data->irq); ++ dev_dbg(&data->pps->dev, "Registered IRQ %d as PPS source\n", ++ data->irq); + + return 0; + } +--- a/drivers/pps/clients/pps-ktimer.c ++++ b/drivers/pps/clients/pps-ktimer.c +@@ -56,7 +56,7 @@ static struct pps_source_info pps_ktimer + + static void __exit pps_ktimer_exit(void) + { +- dev_info(pps->dev, "ktimer PPS source unregistered\n"); ++ dev_dbg(&pps->dev, "ktimer PPS source unregistered\n"); + + del_timer_sync(&ktimer); + pps_unregister_source(pps); +@@ -74,7 +74,7 @@ static int __init pps_ktimer_init(void) + timer_setup(&ktimer, pps_ktimer_event, 0); + mod_timer(&ktimer, jiffies + HZ); + +- dev_info(pps->dev, "ktimer PPS source registered\n"); ++ dev_dbg(&pps->dev, "ktimer PPS source registered\n"); + + return 0; + } +--- a/drivers/pps/clients/pps-ldisc.c ++++ b/drivers/pps/clients/pps-ldisc.c +@@ -34,7 +34,7 @@ static void pps_tty_dcd_change(struct tt + pps_event(pps, &ts, status ? PPS_CAPTUREASSERT : + PPS_CAPTURECLEAR, NULL); + +- dev_dbg(pps->dev, "PPS %s at %lu\n", ++ dev_dbg(&pps->dev, "PPS %s at %lu\n", + status ? "assert" : "clear", jiffies); + } + +@@ -71,7 +71,7 @@ static int pps_tty_open(struct tty_struc + goto err_unregister; + } + +- dev_info(pps->dev, "source \"%s\" added\n", info.path); ++ dev_dbg(&pps->dev, "source \"%s\" added\n", info.path); + + return 0; + +@@ -91,7 +91,7 @@ static void pps_tty_close(struct tty_str + if (WARN_ON(!pps)) + return; + +- dev_info(pps->dev, "removed\n"); ++ dev_info(&pps->dev, "removed\n"); + pps_unregister_source(pps); + } + +--- a/drivers/pps/clients/pps_parport.c ++++ b/drivers/pps/clients/pps_parport.c +@@ -83,7 +83,7 @@ static void parport_irq(void *handle) + /* check the signal (no signal means the pulse is lost this time) */ + if (!signal_is_set(port)) { + local_irq_restore(flags); +- dev_err(dev->pps->dev, "lost the signal\n"); ++ dev_err(&dev->pps->dev, "lost the signal\n"); + goto out_assert; + } + +@@ -100,7 +100,7 @@ static void parport_irq(void *handle) + /* timeout */ + dev->cw_err++; + if (dev->cw_err >= CLEAR_WAIT_MAX_ERRORS) { +- dev_err(dev->pps->dev, "disabled clear edge capture after %d" ++ dev_err(&dev->pps->dev, "disabled clear edge capture after %d" + " timeouts\n", dev->cw_err); + dev->cw = 0; + dev->cw_err = 0; +--- a/drivers/pps/kapi.c ++++ b/drivers/pps/kapi.c +@@ -41,7 +41,7 @@ static void pps_add_offset(struct pps_kt + static void pps_echo_client_default(struct pps_device *pps, int event, + void *data) + { +- dev_info(pps->dev, "echo %s %s\n", ++ dev_info(&pps->dev, "echo %s %s\n", + event & PPS_CAPTUREASSERT ? "assert" : "", + event & PPS_CAPTURECLEAR ? "clear" : ""); + } +@@ -112,7 +112,7 @@ struct pps_device *pps_register_source(s + goto kfree_pps; + } + +- dev_info(pps->dev, "new PPS source %s\n", info->name); ++ dev_dbg(&pps->dev, "new PPS source %s\n", info->name); + + return pps; + +@@ -166,7 +166,7 @@ void pps_event(struct pps_device *pps, s + /* check event type */ + BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0); + +- dev_dbg(pps->dev, "PPS event at %lld.%09ld\n", ++ dev_dbg(&pps->dev, "PPS event at %lld.%09ld\n", + (s64)ts->ts_real.tv_sec, ts->ts_real.tv_nsec); + + timespec_to_pps_ktime(&ts_real, ts->ts_real); +@@ -188,7 +188,7 @@ void pps_event(struct pps_device *pps, s + /* Save the time stamp */ + pps->assert_tu = ts_real; + pps->assert_sequence++; +- dev_dbg(pps->dev, "capture assert seq #%u\n", ++ dev_dbg(&pps->dev, "capture assert seq #%u\n", + pps->assert_sequence); + + captured = ~0; +@@ -202,7 +202,7 @@ void pps_event(struct pps_device *pps, s + /* Save the time stamp */ + pps->clear_tu = ts_real; + pps->clear_sequence++; +- dev_dbg(pps->dev, "capture clear seq #%u\n", ++ dev_dbg(&pps->dev, "capture clear seq #%u\n", + pps->clear_sequence); + + captured = ~0; +--- a/drivers/pps/kc.c ++++ b/drivers/pps/kc.c +@@ -43,11 +43,11 @@ int pps_kc_bind(struct pps_device *pps, + pps_kc_hardpps_mode = 0; + pps_kc_hardpps_dev = NULL; + spin_unlock_irq(&pps_kc_hardpps_lock); +- dev_info(pps->dev, "unbound kernel" ++ dev_info(&pps->dev, "unbound kernel" + " consumer\n"); + } else { + spin_unlock_irq(&pps_kc_hardpps_lock); +- dev_err(pps->dev, "selected kernel consumer" ++ dev_err(&pps->dev, "selected kernel consumer" + " is not bound\n"); + return -EINVAL; + } +@@ -57,11 +57,11 @@ int pps_kc_bind(struct pps_device *pps, + pps_kc_hardpps_mode = bind_args->edge; + pps_kc_hardpps_dev = pps; + spin_unlock_irq(&pps_kc_hardpps_lock); +- dev_info(pps->dev, "bound kernel consumer: " ++ dev_info(&pps->dev, "bound kernel consumer: " + "edge=0x%x\n", bind_args->edge); + } else { + spin_unlock_irq(&pps_kc_hardpps_lock); +- dev_err(pps->dev, "another kernel consumer" ++ dev_err(&pps->dev, "another kernel consumer" + " is already bound\n"); + return -EINVAL; + } +@@ -83,7 +83,7 @@ void pps_kc_remove(struct pps_device *pp + pps_kc_hardpps_mode = 0; + pps_kc_hardpps_dev = NULL; + spin_unlock_irq(&pps_kc_hardpps_lock); +- dev_info(pps->dev, "unbound kernel consumer" ++ dev_info(&pps->dev, "unbound kernel consumer" + " on device removal\n"); + } else + spin_unlock_irq(&pps_kc_hardpps_lock); +--- a/drivers/pps/pps.c ++++ b/drivers/pps/pps.c +@@ -25,7 +25,7 @@ + * Local variables + */ + +-static dev_t pps_devt; ++static int pps_major; + static struct class *pps_class; + + static DEFINE_MUTEX(pps_idr_lock); +@@ -62,7 +62,7 @@ static int pps_cdev_pps_fetch(struct pps + else { + unsigned long ticks; + +- dev_dbg(pps->dev, "timeout %lld.%09d\n", ++ dev_dbg(&pps->dev, "timeout %lld.%09d\n", + (long long) fdata->timeout.sec, + fdata->timeout.nsec); + ticks = fdata->timeout.sec * HZ; +@@ -80,7 +80,7 @@ static int pps_cdev_pps_fetch(struct pps + + /* Check for pending signals */ + if (err == -ERESTARTSYS) { +- dev_dbg(pps->dev, "pending signal caught\n"); ++ dev_dbg(&pps->dev, "pending signal caught\n"); + return -EINTR; + } + +@@ -98,7 +98,7 @@ static long pps_cdev_ioctl(struct file * + + switch (cmd) { + case PPS_GETPARAMS: +- dev_dbg(pps->dev, "PPS_GETPARAMS\n"); ++ dev_dbg(&pps->dev, "PPS_GETPARAMS\n"); + + spin_lock_irq(&pps->lock); + +@@ -114,7 +114,7 @@ static long pps_cdev_ioctl(struct file * + break; + + case PPS_SETPARAMS: +- dev_dbg(pps->dev, "PPS_SETPARAMS\n"); ++ dev_dbg(&pps->dev, "PPS_SETPARAMS\n"); + + /* Check the capabilities */ + if (!capable(CAP_SYS_TIME)) +@@ -124,14 +124,14 @@ static long pps_cdev_ioctl(struct file * + if (err) + return -EFAULT; + if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { +- dev_dbg(pps->dev, "capture mode unspecified (%x)\n", ++ dev_dbg(&pps->dev, "capture mode unspecified (%x)\n", + params.mode); + return -EINVAL; + } + + /* Check for supported capabilities */ + if ((params.mode & ~pps->info.mode) != 0) { +- dev_dbg(pps->dev, "unsupported capabilities (%x)\n", ++ dev_dbg(&pps->dev, "unsupported capabilities (%x)\n", + params.mode); + return -EINVAL; + } +@@ -144,7 +144,7 @@ static long pps_cdev_ioctl(struct file * + /* Restore the read only parameters */ + if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { + /* section 3.3 of RFC 2783 interpreted */ +- dev_dbg(pps->dev, "time format unspecified (%x)\n", ++ dev_dbg(&pps->dev, "time format unspecified (%x)\n", + params.mode); + pps->params.mode |= PPS_TSFMT_TSPEC; + } +@@ -165,7 +165,7 @@ static long pps_cdev_ioctl(struct file * + break; + + case PPS_GETCAP: +- dev_dbg(pps->dev, "PPS_GETCAP\n"); ++ dev_dbg(&pps->dev, "PPS_GETCAP\n"); + + err = put_user(pps->info.mode, iuarg); + if (err) +@@ -176,7 +176,7 @@ static long pps_cdev_ioctl(struct file * + case PPS_FETCH: { + struct pps_fdata fdata; + +- dev_dbg(pps->dev, "PPS_FETCH\n"); ++ dev_dbg(&pps->dev, "PPS_FETCH\n"); + + err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata)); + if (err) +@@ -206,7 +206,7 @@ static long pps_cdev_ioctl(struct file * + case PPS_KC_BIND: { + struct pps_bind_args bind_args; + +- dev_dbg(pps->dev, "PPS_KC_BIND\n"); ++ dev_dbg(&pps->dev, "PPS_KC_BIND\n"); + + /* Check the capabilities */ + if (!capable(CAP_SYS_TIME)) +@@ -218,7 +218,7 @@ static long pps_cdev_ioctl(struct file * + + /* Check for supported capabilities */ + if ((bind_args.edge & ~pps->info.mode) != 0) { +- dev_err(pps->dev, "unsupported capabilities (%x)\n", ++ dev_err(&pps->dev, "unsupported capabilities (%x)\n", + bind_args.edge); + return -EINVAL; + } +@@ -227,7 +227,7 @@ static long pps_cdev_ioctl(struct file * + if (bind_args.tsformat != PPS_TSFMT_TSPEC || + (bind_args.edge & ~PPS_CAPTUREBOTH) != 0 || + bind_args.consumer != PPS_KC_HARDPPS) { +- dev_err(pps->dev, "invalid kernel consumer bind" ++ dev_err(&pps->dev, "invalid kernel consumer bind" + " parameters (%x)\n", bind_args.edge); + return -EINVAL; + } +@@ -259,7 +259,7 @@ static long pps_cdev_compat_ioctl(struct + struct pps_fdata fdata; + int err; + +- dev_dbg(pps->dev, "PPS_FETCH\n"); ++ dev_dbg(&pps->dev, "PPS_FETCH\n"); + + err = copy_from_user(&compat, uarg, sizeof(struct pps_fdata_compat)); + if (err) +@@ -296,20 +296,36 @@ static long pps_cdev_compat_ioctl(struct + #define pps_cdev_compat_ioctl NULL + #endif + ++static struct pps_device *pps_idr_get(unsigned long id) ++{ ++ struct pps_device *pps; ++ ++ mutex_lock(&pps_idr_lock); ++ pps = idr_find(&pps_idr, id); ++ if (pps) ++ get_device(&pps->dev); ++ ++ mutex_unlock(&pps_idr_lock); ++ return pps; ++} ++ + static int pps_cdev_open(struct inode *inode, struct file *file) + { +- struct pps_device *pps = container_of(inode->i_cdev, +- struct pps_device, cdev); ++ struct pps_device *pps = pps_idr_get(iminor(inode)); ++ ++ if (!pps) ++ return -ENODEV; ++ + file->private_data = pps; +- kobject_get(&pps->dev->kobj); + return 0; + } + + static int pps_cdev_release(struct inode *inode, struct file *file) + { +- struct pps_device *pps = container_of(inode->i_cdev, +- struct pps_device, cdev); +- kobject_put(&pps->dev->kobj); ++ struct pps_device *pps = file->private_data; ++ ++ WARN_ON(pps->id != iminor(inode)); ++ put_device(&pps->dev); + return 0; + } + +@@ -332,22 +348,13 @@ static void pps_device_destruct(struct d + { + struct pps_device *pps = dev_get_drvdata(dev); + +- cdev_del(&pps->cdev); +- +- /* Now we can release the ID for re-use */ + pr_debug("deallocating pps%d\n", pps->id); +- mutex_lock(&pps_idr_lock); +- idr_remove(&pps_idr, pps->id); +- mutex_unlock(&pps_idr_lock); +- +- kfree(dev); + kfree(pps); + } + + int pps_register_cdev(struct pps_device *pps) + { + int err; +- dev_t devt; + + mutex_lock(&pps_idr_lock); + /* +@@ -364,40 +371,29 @@ int pps_register_cdev(struct pps_device + goto out_unlock; + } + pps->id = err; +- mutex_unlock(&pps_idr_lock); +- +- devt = MKDEV(MAJOR(pps_devt), pps->id); +- +- cdev_init(&pps->cdev, &pps_cdev_fops); +- pps->cdev.owner = pps->info.owner; + +- err = cdev_add(&pps->cdev, devt, 1); +- if (err) { +- pr_err("%s: failed to add char device %d:%d\n", +- pps->info.name, MAJOR(pps_devt), pps->id); ++ pps->dev.class = pps_class; ++ pps->dev.parent = pps->info.dev; ++ pps->dev.devt = MKDEV(pps_major, pps->id); ++ dev_set_drvdata(&pps->dev, pps); ++ dev_set_name(&pps->dev, "pps%d", pps->id); ++ err = device_register(&pps->dev); ++ if (err) + goto free_idr; +- } +- pps->dev = device_create(pps_class, pps->info.dev, devt, pps, +- "pps%d", pps->id); +- if (IS_ERR(pps->dev)) { +- err = PTR_ERR(pps->dev); +- goto del_cdev; +- } + + /* Override the release function with our own */ +- pps->dev->release = pps_device_destruct; ++ pps->dev.release = pps_device_destruct; + +- pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, +- MAJOR(pps_devt), pps->id); ++ pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, pps_major, ++ pps->id); + ++ get_device(&pps->dev); ++ mutex_unlock(&pps_idr_lock); + return 0; + +-del_cdev: +- cdev_del(&pps->cdev); +- + free_idr: +- mutex_lock(&pps_idr_lock); + idr_remove(&pps_idr, pps->id); ++ put_device(&pps->dev); + out_unlock: + mutex_unlock(&pps_idr_lock); + return err; +@@ -407,7 +403,13 @@ void pps_unregister_cdev(struct pps_devi + { + pr_debug("unregistering pps%d\n", pps->id); + pps->lookup_cookie = NULL; +- device_destroy(pps_class, pps->dev->devt); ++ device_destroy(pps_class, pps->dev.devt); ++ ++ /* Now we can release the ID for re-use */ ++ mutex_lock(&pps_idr_lock); ++ idr_remove(&pps_idr, pps->id); ++ put_device(&pps->dev); ++ mutex_unlock(&pps_idr_lock); + } + + /* +@@ -427,6 +429,11 @@ void pps_unregister_cdev(struct pps_devi + * so that it will not be used again, even if the pps device cannot + * be removed from the idr due to pending references holding the minor + * number in use. ++ * ++ * Since pps_idr holds a reference to the device, the returned ++ * pps_device is guaranteed to be valid until pps_unregister_cdev() is ++ * called on it. But after calling pps_unregister_cdev(), it may be ++ * freed at any time. + */ + struct pps_device *pps_lookup_dev(void const *cookie) + { +@@ -449,13 +456,11 @@ EXPORT_SYMBOL(pps_lookup_dev); + static void __exit pps_exit(void) + { + class_destroy(pps_class); +- unregister_chrdev_region(pps_devt, PPS_MAX_SOURCES); ++ __unregister_chrdev(pps_major, 0, PPS_MAX_SOURCES, "pps"); + } + + static int __init pps_init(void) + { +- int err; +- + pps_class = class_create(THIS_MODULE, "pps"); + if (IS_ERR(pps_class)) { + pr_err("failed to allocate class\n"); +@@ -463,8 +468,9 @@ static int __init pps_init(void) + } + pps_class->dev_groups = pps_groups; + +- err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps"); +- if (err < 0) { ++ pps_major = __register_chrdev(0, 0, PPS_MAX_SOURCES, "pps", ++ &pps_cdev_fops); ++ if (pps_major < 0) { + pr_err("failed to allocate char device region\n"); + goto remove_class; + } +@@ -477,8 +483,7 @@ static int __init pps_init(void) + + remove_class: + class_destroy(pps_class); +- +- return err; ++ return pps_major; + } + + subsys_initcall(pps_init); +--- a/include/linux/pps_kernel.h ++++ b/include/linux/pps_kernel.h +@@ -56,8 +56,7 @@ struct pps_device { + + unsigned int id; /* PPS source unique ID */ + void const *lookup_cookie; /* For pps_lookup_dev() only */ +- struct cdev cdev; +- struct device *dev; ++ struct device dev; + struct fasync_struct *async_queue; /* fasync method */ + spinlock_t lock; + }; diff --git a/queue-5.10/revert-btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch b/queue-5.10/revert-btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch new file mode 100644 index 0000000000..ba5cc0cdf0 --- /dev/null +++ b/queue-5.10/revert-btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch @@ -0,0 +1,36 @@ +From stable+bounces-114127-greg=kroah.com@vger.kernel.org Thu Feb 6 17:20:43 2025 +From: Koichiro Den +Date: Fri, 7 Feb 2025 01:19:54 +0900 +Subject: Revert "btrfs: avoid monopolizing a core when activating a swap file" +To: gregkh@linuxfoundation.org, stable@vger.kernel.org +Cc: wqu@suse.com, fdmanana@suse.com, dsterba@suse.com +Message-ID: <20250206161955.1387041-1-koichiro.den@canonical.com> + +From: Koichiro Den + +This reverts commit a1c3a19446a440c68e80e9c34c5f308ff58aac88. + +The backport for linux-5.10.y, commit a1c3a19446a4 ("btrfs: avoid +monopolizing a core when activating a swap file"), inserted +cond_resched() in the wrong location. + +Revert it now; a subsequent commit will re-backport the original patch. + +Fixes: a1c3a19446a4 ("btrfs: avoid monopolizing a core when activating a swap file") # linux-5.10.y +Signed-off-by: Koichiro Den +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/inode.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -7127,8 +7127,6 @@ noinline int can_nocow_extent(struct ino + ret = -EAGAIN; + goto out; + } +- +- cond_resched(); + } + + btrfs_release_path(path); diff --git a/queue-5.10/series b/queue-5.10/series index 6ee5205d71..b6382c2bdd 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -316,3 +316,11 @@ netdevsim-print-human-readable-ip-address.patch selftests-rtnetlink-update-netdevsim-ipsec-output-format.patch f2fs-fix-to-wait-dio-completion.patch x86-i8253-disable-pit-timer-0-when-not-in-use.patch +revert-btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch +btrfs-avoid-monopolizing-a-core-when-activating-a-swap-file.patch +pps-fix-a-use-after-free.patch +mm-call-the-security_mmap_file-lsm-hook-in-remap_file_pages.patch +ima-fix-use-after-free-on-a-dentry-s-dname.name.patch +vlan-introduce-vlan_dev_free_egress_priority.patch +vlan-move-dev_put-into-vlan_dev_uninit.patch +nvme-pci-fix-multiple-races-in-nvme_setup_io_queues.patch diff --git a/queue-5.10/vlan-introduce-vlan_dev_free_egress_priority.patch b/queue-5.10/vlan-introduce-vlan_dev_free_egress_priority.patch new file mode 100644 index 0000000000..43875f9c9f --- /dev/null +++ b/queue-5.10/vlan-introduce-vlan_dev_free_egress_priority.patch @@ -0,0 +1,83 @@ +From 37aa50c539bcbcc01767e515bd170787fcfc0f33 Mon Sep 17 00:00:00 2001 +From: Xin Long +Date: Wed, 9 Feb 2022 03:19:55 -0500 +Subject: vlan: introduce vlan_dev_free_egress_priority + +From: Xin Long + +commit 37aa50c539bcbcc01767e515bd170787fcfc0f33 upstream. + +This patch is to introduce vlan_dev_free_egress_priority() to +free egress priority for vlan dev, and keep vlan_dev_uninit() +static as .ndo_uninit. It makes the code more clear and safer +when adding new code in vlan_dev_uninit() in the future. + +Signed-off-by: Xin Long +Signed-off-by: David S. Miller +Signed-off-by: Olivier Matz +Signed-off-by: Ivan Delalande +Signed-off-by: Greg Kroah-Hartman +--- + net/8021q/vlan.h | 2 +- + net/8021q/vlan_dev.c | 7 ++++++- + net/8021q/vlan_netlink.c | 7 ++++--- + 3 files changed, 11 insertions(+), 5 deletions(-) + +--- a/net/8021q/vlan.h ++++ b/net/8021q/vlan.h +@@ -124,6 +124,7 @@ void vlan_dev_set_ingress_priority(const + u32 skb_prio, u16 vlan_prio); + int vlan_dev_set_egress_priority(const struct net_device *dev, + u32 skb_prio, u16 vlan_prio); ++void vlan_dev_free_egress_priority(const struct net_device *dev); + int vlan_dev_change_flags(const struct net_device *dev, u32 flag, u32 mask); + void vlan_dev_get_realdev_name(const struct net_device *dev, char *result); + +@@ -133,7 +134,6 @@ int vlan_check_real_dev(struct net_devic + void vlan_setup(struct net_device *dev); + int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack); + void unregister_vlan_dev(struct net_device *dev, struct list_head *head); +-void vlan_dev_uninit(struct net_device *dev); + bool vlan_dev_inherit_address(struct net_device *dev, + struct net_device *real_dev); + +--- a/net/8021q/vlan_dev.c ++++ b/net/8021q/vlan_dev.c +@@ -613,7 +613,7 @@ static int vlan_dev_init(struct net_devi + } + + /* Note: this function might be called multiple times for the same device. */ +-void vlan_dev_uninit(struct net_device *dev) ++void vlan_dev_free_egress_priority(const struct net_device *dev) + { + struct vlan_priority_tci_mapping *pm; + struct vlan_dev_priv *vlan = vlan_dev_priv(dev); +@@ -627,6 +627,11 @@ void vlan_dev_uninit(struct net_device * + } + } + ++static void vlan_dev_uninit(struct net_device *dev) ++{ ++ vlan_dev_free_egress_priority(dev); ++} ++ + static netdev_features_t vlan_dev_fix_features(struct net_device *dev, + netdev_features_t features) + { +--- a/net/8021q/vlan_netlink.c ++++ b/net/8021q/vlan_netlink.c +@@ -187,10 +187,11 @@ static int vlan_newlink(struct net *src_ + return -EINVAL; + + err = vlan_changelink(dev, tb, data, extack); +- if (!err) +- err = register_vlan_dev(dev, extack); + if (err) +- vlan_dev_uninit(dev); ++ return err; ++ err = register_vlan_dev(dev, extack); ++ if (err) ++ vlan_dev_free_egress_priority(dev); + return err; + } + diff --git a/queue-5.10/vlan-move-dev_put-into-vlan_dev_uninit.patch b/queue-5.10/vlan-move-dev_put-into-vlan_dev_uninit.patch new file mode 100644 index 0000000000..52f2def88a --- /dev/null +++ b/queue-5.10/vlan-move-dev_put-into-vlan_dev_uninit.patch @@ -0,0 +1,70 @@ +From d6ff94afd90b0ce8d1715f8ef77d4347d7a7f2c0 Mon Sep 17 00:00:00 2001 +From: Xin Long +Date: Wed, 9 Feb 2022 03:19:56 -0500 +Subject: vlan: move dev_put into vlan_dev_uninit + +From: Xin Long + +commit d6ff94afd90b0ce8d1715f8ef77d4347d7a7f2c0 upstream. + +Shuang Li reported an QinQ issue by simply doing: + + # ip link add dummy0 type dummy + # ip link add link dummy0 name dummy0.1 type vlan id 1 + # ip link add link dummy0.1 name dummy0.1.2 type vlan id 2 + # rmmod 8021q + + unregister_netdevice: waiting for dummy0.1 to become free. Usage count = 1 + +When rmmods 8021q, all vlan devs are deleted from their real_dev's vlan grp +and added into list_kill by unregister_vlan_dev(). dummy0.1 is unregistered +before dummy0.1.2, as it's using for_each_netdev() in __rtnl_kill_links(). + +When unregisters dummy0.1, dummy0.1.2 is not unregistered in the event of +NETDEV_UNREGISTER, as it's been deleted from dummy0.1's vlan grp. However, +due to dummy0.1.2 still holding dummy0.1, dummy0.1 will keep waiting in +netdev_wait_allrefs(), while dummy0.1.2 will never get unregistered and +release dummy0.1, as it delays dev_put until calling dev->priv_destructor, +vlan_dev_free(). + +This issue was introduced by Commit 563bcbae3ba2 ("net: vlan: fix a UAF in +vlan_dev_real_dev()"), and this patch is to fix it by moving dev_put() into +vlan_dev_uninit(), which is called after NETDEV_UNREGISTER event but before +netdev_wait_allrefs(). + +Fixes: 563bcbae3ba2 ("net: vlan: fix a UAF in vlan_dev_real_dev()") +Reported-by: Shuang Li +Signed-off-by: Xin Long +Signed-off-by: David S. Miller +Signed-off-by: Olivier Matz +Signed-off-by: Ivan Delalande +Signed-off-by: Greg Kroah-Hartman +--- + net/8021q/vlan_dev.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/net/8021q/vlan_dev.c ++++ b/net/8021q/vlan_dev.c +@@ -629,7 +629,12 @@ void vlan_dev_free_egress_priority(const + + static void vlan_dev_uninit(struct net_device *dev) + { ++ struct vlan_dev_priv *vlan = vlan_dev_priv(dev); ++ + vlan_dev_free_egress_priority(dev); ++ ++ /* Get rid of the vlan's reference to real_dev */ ++ dev_put(vlan->real_dev); + } + + static netdev_features_t vlan_dev_fix_features(struct net_device *dev, +@@ -821,9 +826,6 @@ static void vlan_dev_free(struct net_dev + + free_percpu(vlan->vlan_pcpu_stats); + vlan->vlan_pcpu_stats = NULL; +- +- /* Get rid of the vlan's reference to real_dev */ +- dev_put(vlan->real_dev); + } + + void vlan_setup(struct net_device *dev)