diff mbox series

[v3,3/4] fbdev: efifb: Cleanup fb_info in .fb_destroy rather than .remove

Message ID 20220505220540.366218-1-javierm@redhat.com (mailing list archive)
State Mainlined
Headers show
Series fbdev: Fix use-after-free caused by wrong fb_info cleanup in drivers | expand

Commit Message

Javier Martinez Canillas May 5, 2022, 10:05 p.m. UTC
The driver is calling framebuffer_release() in its .remove callback, but
this will cause the struct fb_info to be freed too early. Since it could
be that a reference is still hold to it if user-space opened the fbdev.

This would lead to a use-after-free error if the framebuffer device was
unregistered but later a user-space process tries to close the fbdev fd.

To prevent this, move the framebuffer_release() call to fb_ops.fb_destroy
instead of doing it in the driver's .remove callback.

Strictly speaking, the code flow in the driver is still wrong because all
the hardware cleanupd (i.e: iounmap) should be done in .remove while the
software cleanup (i.e: releasing the framebuffer) should be done in the
.fb_destroy handler. But this at least makes to match the behavior before
commit 27599aacbaef ("fbdev: Hot-unplug firmware fb devices on forced removal").

Fixes: 27599aacbaef ("fbdev: Hot-unplug firmware fb devices on forced removal")
Suggested-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---

(no changes since v1)

 drivers/video/fbdev/efifb.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

Comments

Andrzej Hajda May 6, 2022, 1:07 p.m. UTC | #1
On 06.05.2022 00:05, Javier Martinez Canillas wrote:
> The driver is calling framebuffer_release() in its .remove callback, but
> this will cause the struct fb_info to be freed too early. Since it could
> be that a reference is still hold to it if user-space opened the fbdev.
> 
> This would lead to a use-after-free error if the framebuffer device was
> unregistered but later a user-space process tries to close the fbdev fd.
> 
> To prevent this, move the framebuffer_release() call to fb_ops.fb_destroy
> instead of doing it in the driver's .remove callback.
> 
> Strictly speaking, the code flow in the driver is still wrong because all
> the hardware cleanupd (i.e: iounmap) should be done in .remove while the
> software cleanup (i.e: releasing the framebuffer) should be done in the
> .fb_destroy handler. But this at least makes to match the behavior before
> commit 27599aacbaef ("fbdev: Hot-unplug firmware fb devices on forced removal").
> 
> Fixes: 27599aacbaef ("fbdev: Hot-unplug firmware fb devices on forced removal")
> Suggested-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
> Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
> 
> (no changes since v1)
> 
>   drivers/video/fbdev/efifb.c | 9 ++++++++-
>   1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
> index ea42ba6445b2..cfa3dc0b4eee 100644
> --- a/drivers/video/fbdev/efifb.c
> +++ b/drivers/video/fbdev/efifb.c
> @@ -243,6 +243,10 @@ static void efifb_show_boot_graphics(struct fb_info *info)
>   static inline void efifb_show_boot_graphics(struct fb_info *info) {}
>   #endif
>   
> +/*
> + * fb_ops.fb_destroy is called by the last put_fb_info() call at the end
> + * of unregister_framebuffer() or fb_release(). Do any cleanup here.
> + */
>   static void efifb_destroy(struct fb_info *info)
>   {
>   	if (efifb_pci_dev)
> @@ -254,6 +258,9 @@ static void efifb_destroy(struct fb_info *info)
>   		else
>   			memunmap(info->screen_base);
>   	}
> +
> +	framebuffer_release(info);
> +
>   	if (request_mem_succeeded)
>   		release_mem_region(info->apertures->ranges[0].base,
>   				   info->apertures->ranges[0].size);

You are releasing info, then you are using it.

I suspect it is responsible for multiple failures of Intel CI [1].

[1]: 
http://gfx-ci.fi.intel.com/tree/drm-tip/CI_DRM_11615/fi-adl-ddr5/boot0.txt

Regards
Andrzej


> @@ -620,9 +627,9 @@ static int efifb_remove(struct platform_device *pdev)
>   {
>   	struct fb_info *info = platform_get_drvdata(pdev);
>   
> +	/* efifb_destroy takes care of info cleanup */
>   	unregister_framebuffer(info);
>   	sysfs_remove_groups(&pdev->dev.kobj, efifb_groups);
> -	framebuffer_release(info);
>   
>   	return 0;
>   }
Javier Martinez Canillas May 6, 2022, 1:18 p.m. UTC | #2
Hello Andrzej,

On 5/6/22 15:07, Andrzej Hajda wrote:
> On 06.05.2022 00:05, Javier Martinez Canillas wrote:

[snip]

>> +
>> +	framebuffer_release(info);
>> +
>>   	if (request_mem_succeeded)
>>   		release_mem_region(info->apertures->ranges[0].base,
>>   				   info->apertures->ranges[0].size);
> 
> You are releasing info, then you are using it.
> 
> I suspect it is responsible for multiple failures of Intel CI [1].
>

Yes, it is :( sorry about the mess. Ville already reported this to me.
I'll post a patch in a minute.
 
I wonder how this didn't happen before since .remove() happens before
.fb_destroy() AFAIU...
kernel test robot May 8, 2022, 3:40 p.m. UTC | #3
Greeting,

FYI, we noticed the following commit (built with gcc-11):

commit: c6a2b1a99968f3fd74b6b16250d871e5b239e5a8 ("[PATCH v3 3/4] fbdev: efifb: Cleanup fb_info in .fb_destroy rather than .remove")
url: https://github.com/intel-lab-lkp/linux/commits/Javier-Martinez-Canillas/fbdev-Fix-use-after-free-caused-by-wrong-fb_info-cleanup-in-drivers/20220506-060830
base: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git 107c948d1d3e61d10aee9d0f7c3d81bbee9842af
patch link: https://lore.kernel.org/lkml/20220505220540.366218-1-javierm@redhat.com

in testcase: igt
version: igt-x86_64-eddc67c5-1_20220430
with following parameters:

	group: group-07
	ucode: 0xec



on test machine: 12 threads 1 sockets Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz with 32G memory

caused below changes (please refer to attached dmesg/kmsg for entire log/backtrace):



If you fix the issue, kindly add following tag
Reported-by: kernel test robot <oliver.sang@intel.com>


[ 25.615590][ T318] BUG: KASAN: use-after-free in efifb_destroy (drivers/video/fbdev/efifb.c:265) 
[   25.622534][  T318] Read of size 8 at addr ffff888143978358 by task systemd-udevd/318
[   25.630335][  T318]
[   25.632531][  T318] CPU: 8 PID: 318 Comm: systemd-udevd Not tainted 5.18.0-rc5-00019-gc6a2b1a99968 #1
[   25.641707][  T318] Hardware name: Dell Inc. OptiPlex 7060/0C96W1, BIOS 1.4.2 06/11/2019
[   25.641712][  T318] Call Trace:
[   25.641714][  T318]  <TASK>
[ 25.641715][ T318] ? efifb_destroy (drivers/video/fbdev/efifb.c:265) 
[ 25.641722][ T318] dump_stack_lvl (lib/dump_stack.c:107 (discriminator 1)) 
[ 25.641726][ T318] print_address_description+0x1f/0x200 
[ 25.641732][ T318] print_report.cold (mm/kasan/report.c:430) 
[ 25.641736][ T318] ? _raw_spin_lock_irqsave (arch/x86/include/asm/atomic.h:202 include/linux/atomic/atomic-instrumented.h:543 include/asm-generic/qspinlock.h:82 include/linux/spinlock.h:185 include/linux/spinlock_api_smp.h:111 kernel/locking/spinlock.c:162) 
[ 25.687074][ T318] kasan_report (mm/kasan/report.c:162 mm/kasan/report.c:493) 
[ 25.691341][ T318] ? efifb_destroy (drivers/video/fbdev/efifb.c:265) 
[ 25.695954][ T318] efifb_destroy (drivers/video/fbdev/efifb.c:265) 
[ 25.700394][ T318] ? put_fb_info (arch/x86/include/asm/atomic.h:190 include/linux/atomic/atomic-instrumented.h:177 include/linux/refcount.h:272 include/linux/refcount.h:315 include/linux/refcount.h:333 drivers/video/fbdev/core/fbmem.c:79) 
[ 25.704662][ T318] efifb_remove (drivers/video/fbdev/efifb.c:632) 
[ 25.714635][ T318] device_release_driver_internal (drivers/base/dd.c:1202 drivers/base/dd.c:1223) 
1;39mLSB: Load k[ 25.720538][ T318] ? klist_put (include/linux/kref.h:66 lib/klist.c:206 lib/klist.c:217) 
[ 25.726157][ T318] bus_remove_device (drivers/base/bus.c:530) 
[ 25.730943][ T318] device_del (drivers/base/core.c:3593) 
ernel image with[ 25.735119][ T318] ? __device_link_del (drivers/base/core.c:3548) 
[ 25.741431][ T318] ? _printk (kernel/printk/printk.c:2288) 
[ 25.745350][ T318] ? record_print_text.cold (kernel/printk/printk.c:2288) 
[ 25.751663][ T318] platform_device_del (drivers/base/platform.c:760) 
[ 25.757138][ T318] platform_device_unregister (drivers/base/platform.c:547 drivers/base/platform.c:790) 
[ 25.762523][ T318] do_remove_conflicting_framebuffers (drivers/video/fbdev/core/fbmem.c:1591) 
[ 25.768768][ T318] remove_conflicting_framebuffers (drivers/video/fbdev/core/fbmem.c:1780) 
[ 25.774666][ T318] remove_conflicting_pci_framebuffers (drivers/video/fbdev/core/fbmem.c:1879) 
[ 25.780995][ T318] ? __mutex_unlock_slowpath+0x2c0/0x2c0 
[ 25.787501][ T318] ? memcpy (mm/kasan/shadow.c:65 (discriminator 1)) 
[ 25.791331][ T318] drm_aperture_remove_conflicting_pci_framebuffers (drivers/gpu/drm/drm_aperture.c:349) drm
[ 25.799343][ T318] i915_driver_hw_probe (drivers/gpu/drm/i915/i915_driver.c:588) i915
[ 25.805153][ T318] i915_driver_probe (drivers/gpu/drm/i915/i915_driver.c:854) i915
[ 25.810660][ T318] ? __mutex_unlock_slowpath+0x2c0/0x2c0 
[ 25.817167][ T318] ? i915_print_iommu_status (drivers/gpu/drm/i915/i915_driver.c:824) i915
[ 25.823380][ T318] ? drm_privacy_screen_get (drivers/gpu/drm/drm_privacy_screen.c:167) drm
[ 25.829320][ T318] i915_pci_probe (drivers/gpu/drm/i915/i915_pci.c:1191) i915
[ 25.834593][ T318] ? i915_pci_remove (drivers/gpu/drm/i915/i915_pci.c:1191) i915
[ 25.839932][ T318] ? _raw_read_unlock_irqrestore (kernel/locking/spinlock.c:161) 
[ 25.845575][ T318] ? __cond_resched (kernel/sched/core.c:8177) 
[ 25.850098][ T318] ? i915_pci_remove (drivers/gpu/drm/i915/i915_pci.c:1191) i915
[ 25.855450][ T318] local_pci_probe (drivers/pci/pci-driver.c:323) 
[ 25.859973][ T318] pci_call_probe (drivers/pci/pci-driver.c:391) 
[ 25.864496][ T318] ? _raw_spin_lock (arch/x86/include/asm/atomic.h:202 include/linux/atomic/atomic-instrumented.h:543 include/asm-generic/qspinlock.h:82 include/linux/spinlock.h:185 include/linux/spinlock_api_smp.h:134 kernel/locking/spinlock.c:154) 
[ 25.869102][ T318] ? pci_pm_suspend_noirq (drivers/pci/pci-driver.c:351) 
[ 25.874308][ T318] ? pci_assign_irq (drivers/pci/setup-irq.c:25) 
[ 25.878919][ T318] ? pci_match_device (drivers/pci/pci-driver.c:107 drivers/pci/pci-driver.c:158) 
[ 25.883784][ T318] ? kernfs_put (arch/x86/include/asm/atomic.h:123 (discriminator 1) include/linux/atomic/atomic-instrumented.h:576 (discriminator 1) fs/kernfs/dir.c:513 (discriminator 1)) 
[ 25.887959][ T318] pci_device_probe (drivers/pci/pci-driver.c:460) 
[ 25.892575][ T318] ? pci_dma_configure (drivers/pci/pci-driver.c:1620) 
[ 25.897448][ T318] really_probe (drivers/base/dd.c:542 drivers/base/dd.c:621) 
[ 25.901803][ T318] __driver_probe_device (drivers/base/dd.c:752) 
[ 25.906936][ T318] driver_probe_device (drivers/base/dd.c:782) 
[ 25.911806][ T318] __driver_attach (drivers/base/dd.c:1142) 
[ 25.916420][ T318] ? __device_attach_driver (drivers/base/dd.c:1094) 
[ 25.921804][ T318] bus_for_each_dev (drivers/base/bus.c:301) 
[ 25.926500][ T318] ? subsys_dev_iter_exit (drivers/base/bus.c:290) 
[ 25.931541][ T318] ? klist_add_tail (include/linux/list.h:69 include/linux/list.h:102 lib/klist.c:104 lib/klist.c:137) 
[ 25.936234][ T318] bus_add_driver (drivers/base/bus.c:619) 
[ 25.940756][ T318] driver_register (drivers/base/driver.c:171) 
[ 25.945365][ T318] i915_init (drivers/gpu/drm/i915/i915_driver.c:1004) i915
[   25.950136][  T318]  ? 0xffffffffc0b7d000
[ 25.954138][ T318] do_one_initcall (init/main.c:1298) 
[ 25.958665][ T318] ? trace_event_raw_event_initcall_level (init/main.c:1289) 
[ 25.965258][ T318] ? __asan_register_globals (mm/kasan/generic.c:214 (discriminator 3) mm/kasan/generic.c:226 (discriminator 3)) 
[ 25.970556][ T318] ? kasan_unpoison (mm/kasan/shadow.c:108 mm/kasan/shadow.c:142) 
[ 25.975079][ T318] do_init_module (kernel/module.c:3731) 
[ 25.979599][ T318] __do_sys_finit_module (kernel/module.c:4222) 
[ 25.984720][ T318] ? __ia32_sys_init_module (kernel/module.c:4190) 
[ 25.989927][ T318] ? __seccomp_filter (arch/x86/include/asm/bitops.h:214 include/asm-generic/bitops/instrumented-non-atomic.h:135 kernel/seccomp.c:351 kernel/seccomp.c:378 kernel/seccomp.c:410 kernel/seccomp.c:1183) 
[ 25.994794][ T318] ? vm_mmap_pgoff (mm/util.c:523) 
[ 25.999405][ T318] do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80) 
[ 26.003668][ T318] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:115) 
[   26.009398][  T318] RIP: 0033:0x7f280dd1b989
[ 26.013659][ T318] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d d7 64 0c 00 f7 d8 64 89 01 48
All code
========
   0:	00 c3                	add    %al,%bl
   2:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
   9:	00 00 00 
   c:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
  11:	48 89 f8             	mov    %rdi,%rax
  14:	48 89 f7             	mov    %rsi,%rdi
  17:	48 89 d6             	mov    %rdx,%rsi
  1a:	48 89 ca             	mov    %rcx,%rdx
  1d:	4d 89 c2             	mov    %r8,%r10
  20:	4d 89 c8             	mov    %r9,%r8
  23:	4c 8b 4c 24 08       	mov    0x8(%rsp),%r9
  28:	0f 05                	syscall 
  2a:*	48 3d 01 f0 ff ff    	cmp    $0xfffffffffffff001,%rax		<-- trapping instruction
  30:	73 01                	jae    0x33
  32:	c3                   	retq   
  33:	48 8b 0d d7 64 0c 00 	mov    0xc64d7(%rip),%rcx        # 0xc6511
  3a:	f7 d8                	neg    %eax
  3c:	64 89 01             	mov    %eax,%fs:(%rcx)
  3f:	48                   	rex.W

Code starting with the faulting instruction
===========================================
   0:	48 3d 01 f0 ff ff    	cmp    $0xfffffffffffff001,%rax
   6:	73 01                	jae    0x9
   8:	c3                   	retq   
   9:	48 8b 0d d7 64 0c 00 	mov    0xc64d7(%rip),%rcx        # 0xc64e7
  10:	f7 d8                	neg    %eax
  12:	64 89 01             	mov    %eax,%fs:(%rcx)
  15:	48                   	rex.W
[   26.033004][  T318] RSP: 002b:00007ffcd7609228 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[   26.041236][  T318] RAX: ffffffffffffffda RBX: 0000561117222e00 RCX: 00007f280dd1b989
[   26.049030][  T318] RDX: 0000000000000000 RSI: 00007f280dc20cad RDI: 0000000000000018
[   26.056823][  T318] RBP: 00007f280dc20cad R08: 0000000000000000 R09: 0000000000000000
[   26.064618][  T318] R10: 0000000000000018 R11: 0000000000000246 R12: 0000000000000000
[   26.072414][  T318] R13: 000056111726f6d0 R14: 0000000000020000 R15: 0000561117222e00
[   26.080214][  T318]  </TASK>
[   26.083093][  T318]
[   26.085278][  T318] Allocated by task 1:
[ 26.089189][ T318] kasan_save_stack (mm/kasan/common.c:39) 
[ 26.093708][ T318] __kasan_kmalloc (mm/kasan/common.c:45 mm/kasan/common.c:436 mm/kasan/common.c:515 mm/kasan/common.c:524) 
[ 26.098143][ T318] framebuffer_alloc (drivers/video/fbdev/core/fbsysfs.c:49) 
[ 26.102833][ T318] efifb_probe.cold (drivers/video/fbdev/efifb.c:461) 
[ 26.107611][ T318] platform_probe (drivers/base/platform.c:1416) 
[ 26.112048][ T318] really_probe (drivers/base/dd.c:542 drivers/base/dd.c:621) 
[ 26.116399][ T318] __driver_probe_device (drivers/base/dd.c:752) 
[ 26.121521][ T318] driver_probe_device (drivers/base/dd.c:782) 
[ 26.126389][ T318] __device_attach_driver (drivers/base/dd.c:900) 
[ 26.131606][ T318] bus_for_each_drv (drivers/base/bus.c:385 drivers/base/bus.c:426) 
[ 26.136302][ T318] __device_attach (drivers/base/dd.c:970) 
[ 26.140917][ T318] bus_probe_device (drivers/base/bus.c:489) 
[ 26.145617][ T318] device_add (drivers/base/core.c:3412) 
[ 26.149882][ T318] platform_device_add (drivers/base/platform.c:713) 
[ 26.154842][ T318] sysfb_init (drivers/firmware/sysfb.c:72) 
[ 26.158844][ T318] do_one_initcall (init/main.c:1298) 
[ 26.163364][ T318] do_initcalls (init/main.c:1370 init/main.c:1387) 
[ 26.167709][ T318] kernel_init_freeable (init/main.c:1617) 
[ 26.172747][ T318] kernel_init (init/main.c:1504) 
[ 26.176921][ T318] ret_from_fork (arch/x86/entry/entry_64.S:304) 
[   26.181184][  T318]
[   26.183379][  T318] Freed by task 318:
[ 26.187123][ T318] kasan_save_stack (mm/kasan/common.c:39) 
[ 26.191644][ T318] kasan_set_track (mm/kasan/common.c:45) 
[ 26.196082][ T318] kasan_set_free_info (mm/kasan/generic.c:372) 
[ 26.200864][ T318] __kasan_slab_free (mm/kasan/common.c:368 mm/kasan/common.c:328 mm/kasan/common.c:374) 
[ 26.205644][ T318] kfree (mm/slub.c:1754 mm/slub.c:3510 mm/slub.c:4552) 
[ 26.209302][ T318] efifb_destroy (drivers/video/fbdev/efifb.c:264) 
[ 26.213651][ T318] efifb_remove (drivers/video/fbdev/efifb.c:632) 
[ 26.217829][ T318] platform_remove (drivers/base/platform.c:1438) 
[ 26.222265][ T318] device_release_driver_internal (drivers/base/dd.c:1202 drivers/base/dd.c:1223) 
[ 26.228165][ T318] bus_remove_device (drivers/base/bus.c:530) 
[ 26.232943][ T318] device_del (drivers/base/core.c:3593) 
[ 26.237116][ T318] platform_device_del (drivers/base/platform.c:760) 
[ 26.242585][ T318] platform_device_unregister (drivers/base/platform.c:547 drivers/base/platform.c:790) 


To reproduce:

        git clone https://github.com/intel/lkp-tests.git
        cd lkp-tests
        sudo bin/lkp install job.yaml           # job file is attached in this email
        bin/lkp split-job --compatible job.yaml # generate the yaml file for lkp run
        sudo bin/lkp run generated-yaml-file

        # if come across any failure that blocks the test,
        # please remove ~/.lkp and /lkp dir to run from a clean state.
diff mbox series

Patch

diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
index ea42ba6445b2..cfa3dc0b4eee 100644
--- a/drivers/video/fbdev/efifb.c
+++ b/drivers/video/fbdev/efifb.c
@@ -243,6 +243,10 @@  static void efifb_show_boot_graphics(struct fb_info *info)
 static inline void efifb_show_boot_graphics(struct fb_info *info) {}
 #endif
 
+/*
+ * fb_ops.fb_destroy is called by the last put_fb_info() call at the end
+ * of unregister_framebuffer() or fb_release(). Do any cleanup here.
+ */
 static void efifb_destroy(struct fb_info *info)
 {
 	if (efifb_pci_dev)
@@ -254,6 +258,9 @@  static void efifb_destroy(struct fb_info *info)
 		else
 			memunmap(info->screen_base);
 	}
+
+	framebuffer_release(info);
+
 	if (request_mem_succeeded)
 		release_mem_region(info->apertures->ranges[0].base,
 				   info->apertures->ranges[0].size);
@@ -620,9 +627,9 @@  static int efifb_remove(struct platform_device *pdev)
 {
 	struct fb_info *info = platform_get_drvdata(pdev);
 
+	/* efifb_destroy takes care of info cleanup */
 	unregister_framebuffer(info);
 	sysfs_remove_groups(&pdev->dev.kobj, efifb_groups);
-	framebuffer_release(info);
 
 	return 0;
 }