[PATCH OLK-5.10 0/3] CVE-2026-46065
CVE-2026-46065 Liu Kai (1): fbdev: defio: fix KABI breakage in struct fb_deferred_io and struct fb_info Thomas Zimmermann (2): fbdev: Refactor implementation of page_mkwrite fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info drivers/video/fbdev/core/fb_defio.c | 184 ++++++++++++++++++++++++---- include/linux/fb.h | 3 +- include/linux/workqueue.h | 4 + 3 files changed, 166 insertions(+), 25 deletions(-) -- 2.34.1
From: Thomas Zimmermann <tzimmermann@suse.de> mainline inclusion from mainline-v5.19-rc1 commit 3ed3811283ddbc959db564efce6f0988c63bc03c category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15309 CVE: CVE-2026-46065 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Refactor the page-write handler for deferred I/O. Drivers use the function to let fbdev track written pages of mmap'ed framebuffer memory. v3: * keep locking within track-pages function for readability (Sam) v2: * don't export the helper until we have an external caller Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20220429100834.18898-4-tzimmer... Conflicts: drivers/video/fbdev/core/fb_defio.c [omitting the "offset" variable in fb_deferred_io_mkwrite() because commit 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct") is not backported] Signed-off-by: Liu Kai <liukai284@huawei.com> --- drivers/video/fbdev/core/fb_defio.c | 50 ++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 0708e214c5a3..577b8c7f7719 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -85,22 +85,16 @@ int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasy } EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); -/* vm_ops->page_mkwrite handler */ -static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf) +/* + * Adds a page to the dirty list. Call this from struct + * vm_operations_struct.page_mkwrite. + */ +static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset, + struct page *page) { - struct page *page = vmf->page; - struct fb_info *info = vmf->vma->vm_private_data; struct fb_deferred_io *fbdefio = info->fbdefio; struct page *cur; - /* this is a callback we get when userspace first tries to - write to the page. we schedule a workqueue. that workqueue - will eventually mkclean the touched pages and execute the - deferred framebuffer IO. then if userspace touches a page - again, we repeat the same scheme */ - - file_update_time(vmf->vma->vm_file); - /* protect against the workqueue changing the page list */ mutex_lock(&fbdefio->lock); @@ -142,6 +136,38 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf) return VM_FAULT_LOCKED; } +/* + * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O + * @fb_info: The fbdev info structure + * @vmf: The VM fault + * + * This is a callback we get when userspace first tries to + * write to the page. We schedule a workqueue. That workqueue + * will eventually mkclean the touched pages and execute the + * deferred framebuffer IO. Then if userspace touches a page + * again, we repeat the same scheme. + * + * Returns: + * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise. + */ +static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf) +{ + unsigned long offset = vmf->address - vmf->vma->vm_start; + struct page *page = vmf->page; + + file_update_time(vmf->vma->vm_file); + + return fb_deferred_io_track_page(info, offset, page); +} + +/* vm_ops->page_mkwrite handler */ +static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf) +{ + struct fb_info *info = vmf->vma->vm_private_data; + + return fb_deferred_io_page_mkwrite(info, vmf); +} + static const struct vm_operations_struct fb_deferred_io_vm_ops = { .fault = fb_deferred_io_fault, .page_mkwrite = fb_deferred_io_mkwrite, -- 2.34.1
From: Thomas Zimmermann <tzimmermann@suse.de> mainline inclusion from mainline-v7.1-rc1 commit 9ded47ad003f09a94b6a710b5c47f4aa5ceb7429 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15309 CVE: CVE-2026-46065 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an instance as part of initializing deferred I/O and remove it only after the final mapping has been closed. If the fb_info and the contained deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info to invalidate the mapping. Any access will then result in a SIGBUS signal. Fixes a long-standing problem, where a device hot-unplug happens while user space still has an active mapping of the graphics memory. The hot- unplug frees the instance of struct fb_info. Accessing the memory will operate on undefined state. Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support") Cc: Helge Deller <deller@gmx.de> Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: stable@vger.kernel.org # v2.6.22+ Signed-off-by: Helge Deller <deller@gmx.de> Conflicts: drivers/video/fbdev/core/fb_defio.c include/linux/fb.h [delete "struct mutex lock" in "struct fb_deferred_io", replace kzalloc_obj() with kzalloc() according to 2932ba8d9c99 ("slab: Introduce kmalloc_obj() and family"), replace vm_flags_set(vma, VM_IO) with vma->vm_flags |= VM_IO according to 1c71222e5f23 ("mm: replace vma->vm_flags direct modifications with modifier calls"), replace fb_deferred_io_get_page() with fb_deferred_io_page() according to 1ecbc7dd2902 ("fbdev/deferred-io: Always call get_page() for framebuffer pages"), delete -ENOMEM in fb_deferred_io_init() according to 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct")] Signed-off-by: Liu Kai <liukai284@huawei.com> --- drivers/video/fbdev/core/fb_defio.c | 178 ++++++++++++++++++++++------ include/linux/fb.h | 4 +- 2 files changed, 147 insertions(+), 35 deletions(-) diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 577b8c7f7719..7fc5ff7ec1f6 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -23,6 +23,75 @@ #include <linux/rmap.h> #include <linux/pagemap.h> +/* + * struct fb_deferred_io_state + */ + +struct fb_deferred_io_state { + struct kref ref; + + struct mutex lock; /* mutex that protects the pageref list */ + /* fields protected by lock */ + struct fb_info *info; +}; + +static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void) +{ + struct fb_deferred_io_state *fbdefio_state; + + fbdefio_state = kzalloc(sizeof(*fbdefio_state), GFP_KERNEL); + if (!fbdefio_state) + return NULL; + + kref_init(&fbdefio_state->ref); + mutex_init(&fbdefio_state->lock); + + return fbdefio_state; +} + +static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state) +{ + mutex_destroy(&fbdefio_state->lock); + + kfree(fbdefio_state); +} + +static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state) +{ + kref_get(&fbdefio_state->ref); +} + +static void __fb_deferred_io_state_release(struct kref *ref) +{ + struct fb_deferred_io_state *fbdefio_state = + container_of(ref, struct fb_deferred_io_state, ref); + + fb_deferred_io_state_release(fbdefio_state); +} + +static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state) +{ + kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release); +} + +/* + * struct vm_operations_struct + */ + +static void fb_deferred_io_vm_open(struct vm_area_struct *vma) +{ + struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data; + + fb_deferred_io_state_get(fbdefio_state); +} + +static void fb_deferred_io_vm_close(struct vm_area_struct *vma) +{ + struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data; + + fb_deferred_io_state_put(fbdefio_state); +} + static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) { void *screen_base = (void __force *) info->screen_base; @@ -39,17 +108,31 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs /* this is to find and return the vmalloc-ed fb pages */ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf) { + struct fb_info *info; unsigned long offset; struct page *page; - struct fb_info *info = vmf->vma->vm_private_data; + vm_fault_t ret; + struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data; + + mutex_lock(&fbdefio_state->lock); + + info = fbdefio_state->info; + if (!info) { + ret = VM_FAULT_SIGBUS; /* our device is gone */ + goto err_mutex_unlock; + } offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= info->fix.smem_len) - return VM_FAULT_SIGBUS; + if (offset >= info->fix.smem_len) { + ret = VM_FAULT_SIGBUS; + goto err_mutex_unlock; + } page = fb_deferred_io_page(info, offset); - if (!page) - return VM_FAULT_SIGBUS; + if (!page) { + ret = VM_FAULT_SIGBUS; + goto err_mutex_unlock; + } get_page(page); @@ -61,8 +144,15 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf) BUG_ON(!page->mapping); page->index = vmf->pgoff; + mutex_unlock(&fbdefio_state->lock); + vmf->page = page; + return 0; + +err_mutex_unlock: + mutex_unlock(&fbdefio_state->lock); + return ret; } int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync) @@ -89,14 +179,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); * Adds a page to the dirty list. Call this from struct * vm_operations_struct.page_mkwrite. */ -static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset, - struct page *page) +static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state, + unsigned long offset, struct page *page) { - struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_info *info; + struct fb_deferred_io *fbdefio; struct page *cur; + vm_fault_t ret; /* protect against the workqueue changing the page list */ - mutex_lock(&fbdefio->lock); + mutex_lock(&fbdefio_state->lock); + + info = fbdefio_state->info; + if (!info) { + ret = VM_FAULT_SIGBUS; /* our device is gone */ + goto err_mutex_unlock; + } + + fbdefio = info->fbdefio; /* first write in this cycle, notify the driver */ if (fbdefio->first_io && list_empty(&fbdefio->pagelist)) @@ -129,46 +229,38 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long list_add_tail(&page->lru, &cur->lru); page_already_added: - mutex_unlock(&fbdefio->lock); + mutex_unlock(&fbdefio_state->lock); /* come back after delay to process the deferred IO */ schedule_delayed_work(&info->deferred_work, fbdefio->delay); return VM_FAULT_LOCKED; + +err_mutex_unlock: + mutex_unlock(&fbdefio_state->lock); + return ret; } -/* - * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O - * @fb_info: The fbdev info structure - * @vmf: The VM fault - * - * This is a callback we get when userspace first tries to - * write to the page. We schedule a workqueue. That workqueue - * will eventually mkclean the touched pages and execute the - * deferred framebuffer IO. Then if userspace touches a page - * again, we repeat the same scheme. - * - * Returns: - * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise. - */ -static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf) +static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state, + struct vm_fault *vmf) { unsigned long offset = vmf->address - vmf->vma->vm_start; struct page *page = vmf->page; file_update_time(vmf->vma->vm_file); - return fb_deferred_io_track_page(info, offset, page); + return fb_deferred_io_track_page(fbdefio_state, offset, page); } -/* vm_ops->page_mkwrite handler */ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf) { - struct fb_info *info = vmf->vma->vm_private_data; + struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data; - return fb_deferred_io_page_mkwrite(info, vmf); + return fb_deferred_io_page_mkwrite(fbdefio_state, vmf); } static const struct vm_operations_struct fb_deferred_io_vm_ops = { + .open = fb_deferred_io_vm_open, + .close = fb_deferred_io_vm_close, .fault = fb_deferred_io_fault, .page_mkwrite = fb_deferred_io_mkwrite, }; @@ -190,7 +282,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; if (!(info->flags & FBINFO_VIRTFB)) vma->vm_flags |= VM_IO; - vma->vm_private_data = info; + vma->vm_private_data = info->fbdefio_state; + + fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */ + return 0; } @@ -202,9 +297,10 @@ static void fb_deferred_io_work(struct work_struct *work) struct list_head *node, *next; struct page *cur; struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; /* here we mkclean the pages, then do all deferred IO */ - mutex_lock(&fbdefio->lock); + mutex_lock(&fbdefio_state->lock); list_for_each_entry(cur, &fbdefio->pagelist, lru) { lock_page(cur); page_mkclean(cur); @@ -218,19 +314,26 @@ static void fb_deferred_io_work(struct work_struct *work) list_for_each_safe(node, next, &fbdefio->pagelist) { list_del(node); } - mutex_unlock(&fbdefio->lock); + mutex_unlock(&fbdefio_state->lock); } void fb_deferred_io_init(struct fb_info *info) { struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_state *fbdefio_state; BUG_ON(!fbdefio); - mutex_init(&fbdefio->lock); + fbdefio_state = fb_deferred_io_state_alloc(); + if (!fbdefio_state) + return; + fbdefio_state->info = info; + INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work); INIT_LIST_HEAD(&fbdefio->pagelist); if (fbdefio->delay == 0) /* set a default of 1 s */ fbdefio->delay = HZ; + + info->fbdefio_state = fbdefio_state; } EXPORT_SYMBOL_GPL(fb_deferred_io_init); @@ -247,6 +350,7 @@ void fb_deferred_io_cleanup(struct fb_info *info) struct fb_deferred_io *fbdefio = info->fbdefio; struct page *page; int i; + struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; BUG_ON(!fbdefio); cancel_delayed_work_sync(&info->deferred_work); @@ -257,6 +361,12 @@ void fb_deferred_io_cleanup(struct fb_info *info) page->mapping = NULL; } - mutex_destroy(&fbdefio->lock); + info->fbdefio_state = NULL; + + mutex_lock(&fbdefio_state->lock); + fbdefio_state->info = NULL; + mutex_unlock(&fbdefio_state->lock); + + fb_deferred_io_state_put(fbdefio_state); } EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup); diff --git a/include/linux/fb.h b/include/linux/fb.h index 916a4b4e5e84..7cf2c03e83f6 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -203,12 +203,13 @@ struct fb_pixmap { struct fb_deferred_io { /* delay between mkwrite and deferred handler */ unsigned long delay; - struct mutex lock; /* mutex that protects the page list */ struct list_head pagelist; /* list of touched pages */ /* callback */ void (*first_io)(struct fb_info *info); void (*deferred_io)(struct fb_info *info, struct list_head *pagelist); }; + +struct fb_deferred_io_state; #endif /* @@ -468,6 +469,7 @@ struct fb_info { #ifdef CONFIG_FB_DEFERRED_IO struct delayed_work deferred_work; struct fb_deferred_io *fbdefio; + struct fb_deferred_io_state *fbdefio_state; #endif const struct fb_ops *fbops; -- 2.34.1
hulk inclusion category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15309 -------------------------------- Fix the KABI breakage in 'struct fb_deferred_io' and 'struct fb_info' caused by the commit aa653458e2cc ("fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info"). Use the KABI_RESERVE() macro defined within struct work_struct for the queue member of struct fb_info. Fixes: aa653458e2cc ("fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info") Signed-off-by: Liu Kai <liukai284@huawei.com> --- drivers/video/fbdev/core/fb_defio.c | 12 ++++++------ include/linux/fb.h | 1 - include/linux/workqueue.h | 4 ++++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 7fc5ff7ec1f6..5b81d2d35398 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -282,9 +282,9 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; if (!(info->flags & FBINFO_VIRTFB)) vma->vm_flags |= VM_IO; - vma->vm_private_data = info->fbdefio_state; + vma->vm_private_data = info->queue.fbdefio_state; - fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */ + fb_deferred_io_state_get(info->queue.fbdefio_state); /* released in vma->vm_ops->close() */ return 0; } @@ -297,7 +297,7 @@ static void fb_deferred_io_work(struct work_struct *work) struct list_head *node, *next; struct page *cur; struct fb_deferred_io *fbdefio = info->fbdefio; - struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; + struct fb_deferred_io_state *fbdefio_state = info->queue.fbdefio_state; /* here we mkclean the pages, then do all deferred IO */ mutex_lock(&fbdefio_state->lock); @@ -333,7 +333,7 @@ void fb_deferred_io_init(struct fb_info *info) if (fbdefio->delay == 0) /* set a default of 1 s */ fbdefio->delay = HZ; - info->fbdefio_state = fbdefio_state; + info->queue.fbdefio_state = fbdefio_state; } EXPORT_SYMBOL_GPL(fb_deferred_io_init); @@ -350,7 +350,7 @@ void fb_deferred_io_cleanup(struct fb_info *info) struct fb_deferred_io *fbdefio = info->fbdefio; struct page *page; int i; - struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; + struct fb_deferred_io_state *fbdefio_state = info->queue.fbdefio_state; BUG_ON(!fbdefio); cancel_delayed_work_sync(&info->deferred_work); @@ -361,7 +361,7 @@ void fb_deferred_io_cleanup(struct fb_info *info) page->mapping = NULL; } - info->fbdefio_state = NULL; + info->queue.fbdefio_state = NULL; mutex_lock(&fbdefio_state->lock); fbdefio_state->info = NULL; diff --git a/include/linux/fb.h b/include/linux/fb.h index 7cf2c03e83f6..0581576d972f 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -469,7 +469,6 @@ struct fb_info { #ifdef CONFIG_FB_DEFERRED_IO struct delayed_work deferred_work; struct fb_deferred_io *fbdefio; - struct fb_deferred_io_state *fbdefio_state; #endif const struct fb_ops *fbops; diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 0ffefea3a557..4fb896d7b839 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -104,7 +104,11 @@ struct work_struct { struct lockdep_map lockdep_map; #endif +#ifdef CONFIG_FB_DEFERRED_IO + KABI_USE(1, struct fb_deferred_io_state *fbdefio_state) +#else KABI_RESERVE(1) +#endif KABI_RESERVE(2) KABI_RESERVE(3) KABI_RESERVE(4) -- 2.34.1
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/23685 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/U4Z... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/23685 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/U4Z...
participants (2)
-
Liu Kai -
patchwork bot