[PATCH OLK-6.6 0/5] CVE-2026-46065
CVE-2026-46065 Liu Kai (2): fbdev: defio: fix KABI breakage in struct fb_deferred_io and struct fb_info fbdev: defio: Remove unused variable 'fbdefio' to fix build warning Thomas Zimmermann (3): fbdev/deferred-io: Clean up pageref on lastclose fbdev/deferred-io: Always call get_page() for framebuffer pages fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info drivers/video/fbdev/core/fb_defio.c | 205 +++++++++++++++++++++------- include/linux/fb.h | 8 +- 2 files changed, 165 insertions(+), 48 deletions(-) -- 2.34.1
From: Thomas Zimmermann <tzimmermann@suse.de> mainline inclusion from mainline-v6.11-rc1 commit 28aea43c705af174b98d01d299bb189c2ccbe085 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... -------------------------------- Clean up the pageref state as part of the lastclose helper. This only requires to clear the page's mapping field. The pageref and page can stay in place for the next opened instance of the frame- buffer file. With the change in the clean-up logic, there's no further need to look up pages during the lastclose cleanup. The code instead uses the existing pagerefs in its look-up table. It also avoids using smem_len, which some driver might not set correctly. v2: - fix typos in commit message (Javier) Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20240419083331.7761-4-tzimmerm... Conflicts: drivers/video/fbdev/core/fb_defio.c [delete context conflicts upper the fb_deferred_io_pageref_clear()] Signed-off-by: Liu Kai <liukai284@huawei.com> --- drivers/video/fbdev/core/fb_defio.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index b9607d5a370d..ec3db7851876 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -36,6 +36,14 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs return page; } +static void fb_deferred_io_pageref_clear(struct fb_deferred_io_pageref *pageref) +{ + struct page *page = pageref->page; + + if (page) + page->mapping = NULL; +} + static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info *info, unsigned long offset, struct page *page) @@ -310,16 +318,13 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_open); static void fb_deferred_io_lastclose(struct fb_info *info) { - struct page *page; - int i; + unsigned long i; flush_delayed_work(&info->deferred_work); /* clear out the mapping that we setup */ - for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) { - page = fb_deferred_io_page(info, i); - page->mapping = NULL; - } + for (i = 0; i < info->npagerefs; ++i) + fb_deferred_io_pageref_clear(&info->pagerefs[i]); } void fb_deferred_io_release(struct fb_info *info) -- 2.34.1
From: Thomas Zimmermann <tzimmermann@suse.de> mainline inclusion from mainline-v6.11-rc1 commit 1ecbc7dd29020d335e1f6f789c23b36a84cd5214 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... -------------------------------- Unconditionally call get_page() after looking up a page from the framebuffer memory. Guarantees that we always hold a reference. This change also refactors the code such that it can support a driver-supplied get_page helper. This will be useful for DRM's fbdev emulation. Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com> Link: https://patchwork.freedesktop.org/patch/msgid/20240419083331.7761-7-tzimmerm... Conflicts: drivers/video/fbdev/core/fb_defio.c [cherry-pick applied cleanly without conflicts, but the patch context differs.] Signed-off-by: Liu Kai <liukai284@huawei.com> --- drivers/video/fbdev/core/fb_defio.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index ec3db7851876..9540a9841a0c 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -23,7 +23,7 @@ #include <linux/rmap.h> #include <linux/pagemap.h> -static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) +static struct page *fb_deferred_io_get_page(struct fb_info *info, unsigned long offs) { void *screen_base = (void __force *) info->screen_base; struct page *page; @@ -33,6 +33,9 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs else page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT); + if (page) + get_page(page); + return page; } @@ -109,12 +112,10 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf) if (offset >= info->fix.smem_len) return VM_FAULT_SIGBUS; - page = fb_deferred_io_page(info, offset); + page = fb_deferred_io_get_page(info, offset); if (!page) return VM_FAULT_SIGBUS; - get_page(page); - if (vmf->vma->vm_file) page->mapping = vmf->vma->vm_file->f_mapping; else -- 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: include/linux/fb.h drivers/video/fbdev/core/fb_defio.c [replace fbdefio with fbdefio_state in fb_deferred_io_work(), replace kzalloc_obj() with kzalloc() in fb_deferred_io_state_alloc()] Signed-off-by: Liu Kai <liukai284@huawei.com> --- drivers/video/fbdev/core/fb_defio.c | 178 ++++++++++++++++++++++------ include/linux/fb.h | 4 +- 2 files changed, 145 insertions(+), 37 deletions(-) diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index 9540a9841a0c..b4ed8a79e4e9 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_get_page(struct fb_info *info, unsigned long offs) { void *screen_base = (void __force *) info->screen_base; @@ -104,17 +173,31 @@ static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref, /* 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_get_page(info, offset); - if (!page) - return VM_FAULT_SIGBUS; + if (!page) { + ret = VM_FAULT_SIGBUS; + goto err_mutex_unlock; + } if (vmf->vma->vm_file) page->mapping = vmf->vma->vm_file->f_mapping; @@ -124,8 +207,15 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf) BUG_ON(!page->mapping); page->index = vmf->pgoff; /* for page_mkclean() */ + 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) @@ -152,15 +242,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 fb_deferred_io_pageref *pageref; 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; pageref = fb_deferred_io_pageref_get(info, offset, page); if (WARN_ON_ONCE(!pageref)) { @@ -178,50 +277,38 @@ static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long */ lock_page(pageref->page); - 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->lock); + 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->pgoff << PAGE_SHIFT; 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, }; @@ -236,7 +323,10 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP); if (!(info->flags & FBINFO_VIRTFB)) vm_flags_set(vma, 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; } EXPORT_SYMBOL_GPL(fb_deferred_io_mmap); @@ -247,9 +337,10 @@ static void fb_deferred_io_work(struct work_struct *work) struct fb_info *info = container_of(work, struct fb_info, deferred_work.work); struct fb_deferred_io_pageref *pageref, *next; 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(pageref, &fbdefio->pagereflist, list) { struct page *cur = pageref->page; lock_page(cur); @@ -264,12 +355,13 @@ static void fb_deferred_io_work(struct work_struct *work) list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list) fb_deferred_io_pageref_put(pageref, info); - mutex_unlock(&fbdefio->lock); + mutex_unlock(&fbdefio_state->lock); } int fb_deferred_io_init(struct fb_info *info) { struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_state *fbdefio_state; struct fb_deferred_io_pageref *pagerefs; unsigned long npagerefs, i; int ret; @@ -279,7 +371,11 @@ int fb_deferred_io_init(struct fb_info *info) if (WARN_ON(!info->fix.smem_len)) return -EINVAL; - mutex_init(&fbdefio->lock); + fbdefio_state = fb_deferred_io_state_alloc(); + if (!fbdefio_state) + return -ENOMEM; + fbdefio_state->info = info; + INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work); INIT_LIST_HEAD(&fbdefio->pagereflist); if (fbdefio->delay == 0) /* set a default of 1 s */ @@ -298,10 +394,12 @@ int fb_deferred_io_init(struct fb_info *info) info->npagerefs = npagerefs; info->pagerefs = pagerefs; + info->fbdefio_state = fbdefio_state; + return 0; err: - mutex_destroy(&fbdefio->lock); + fb_deferred_io_state_release(fbdefio_state); return ret; } EXPORT_SYMBOL_GPL(fb_deferred_io_init); @@ -340,10 +438,18 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release); void fb_deferred_io_cleanup(struct fb_info *info) { struct fb_deferred_io *fbdefio = info->fbdefio; + struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; fb_deferred_io_lastclose(info); + 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); + kvfree(info->pagerefs); - mutex_destroy(&fbdefio->lock); } EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup); diff --git a/include/linux/fb.h b/include/linux/fb.h index 2b917f2a2400..ff2628c846be 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -215,13 +215,14 @@ struct fb_deferred_io { unsigned long delay; bool sort_pagereflist; /* sort pagelist by offset */ int open_count; /* number of opened files; protected by fb_info lock */ - struct mutex lock; /* mutex that protects the pageref list */ struct list_head pagereflist; /* list of pagerefs for touched pages */ /* callback */ void (*deferred_io)(struct fb_info *info, struct list_head *pagelist); KABI_RESERVE(1) KABI_RESERVE(2) }; + +struct fb_deferred_io_state; #endif /* @@ -479,6 +480,7 @@ struct fb_info { unsigned long npagerefs; struct fb_deferred_io_pageref *pagerefs; 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 e5d43517c4c1 ("fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info") Fixes: e5d43517c4c1 ("fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info") Signed-off-by: Liu Kai <liukai284@huawei.com> --- include/linux/fb.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/linux/fb.h b/include/linux/fb.h index ff2628c846be..0638db3a6358 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -215,6 +215,7 @@ struct fb_deferred_io { unsigned long delay; bool sort_pagereflist; /* sort pagelist by offset */ int open_count; /* number of opened files; protected by fb_info lock */ + KABI_BROKEN_REMOVE(struct mutex lock) struct list_head pagereflist; /* list of pagerefs for touched pages */ /* callback */ void (*deferred_io)(struct fb_info *info, struct list_head *pagelist); @@ -480,7 +481,6 @@ struct fb_info { unsigned long npagerefs; struct fb_deferred_io_pageref *pagerefs; struct fb_deferred_io *fbdefio; - struct fb_deferred_io_state *fbdefio_state; #endif const struct fb_ops *fbops; @@ -506,7 +506,11 @@ struct fb_info { void *par; bool skip_vt_switch; /* no VT switch on suspend/resume required */ +#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
hulk inclusion category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15309 -------------------------------- Commit e5d43517c4c1 ("fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info") removed the mutex_destroy() call for fbdefio->lock. The prerequisite patch, 6cdef2ddce2b ("fb_defio: do not use deprecated page->mapping, index fields"), was intentionally excluded to avoid KABI breakage. As a result, the 'fbdefio' variable is no longer used in this context. Remove the 'fbdefio' variable declaration entirely to silence the following compiler warning: warning: unused variable 'fbdefio' [-Wunused-variable] Fixes: e5d43517c4c1 ("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 | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c index b4ed8a79e4e9..753c36141d97 100644 --- a/drivers/video/fbdev/core/fb_defio.c +++ b/drivers/video/fbdev/core/fb_defio.c @@ -437,7 +437,6 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release); void fb_deferred_io_cleanup(struct fb_info *info) { - struct fb_deferred_io *fbdefio = info->fbdefio; struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; fb_deferred_io_lastclose(info); -- 2.34.1
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/23686 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/SSE... 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/23686 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/SSE...
participants (2)
-
Liu Kai -
patchwork bot