Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fbdev: simplefb: Fix use after free in simplefb_detach_genpds()

The pm_domain cleanup can not be devres managed as it uses struct
simplefb_par which is allocated within struct fb_info by
framebuffer_alloc(). This allocation is explicitly freed by
unregister_framebuffer() in simplefb_remove().
Devres managed cleanup runs after the device remove call and thus can no
longer access struct simplefb_par.
Call simplefb_detach_genpds() explicitly from simplefb_destroy() like
the cleanup functions for clocks and regulators.

Fixes an use after free on M2 Mac mini during
aperture_remove_conflicting_devices() using the downstream asahi kernel
with Debian's kernel config. For unknown reasons this started to
consistently dereference an invalid pointer in v6.16.3 based kernels.

[ 6.736134] BUG: KASAN: slab-use-after-free in simplefb_detach_genpds+0x58/0x220
[ 6.743545] Read of size 4 at addr ffff8000304743f0 by task (udev-worker)/227
[ 6.750697]
[ 6.752182] CPU: 6 UID: 0 PID: 227 Comm: (udev-worker) Tainted: G S 6.16.3-asahi+ #16 PREEMPTLAZY
[ 6.752186] Tainted: [S]=CPU_OUT_OF_SPEC
[ 6.752187] Hardware name: Apple Mac mini (M2, 2023) (DT)
[ 6.752189] Call trace:
[ 6.752190] show_stack+0x34/0x98 (C)
[ 6.752194] dump_stack_lvl+0x60/0x80
[ 6.752197] print_report+0x17c/0x4d8
[ 6.752201] kasan_report+0xb4/0x100
[ 6.752206] __asan_report_load4_noabort+0x20/0x30
[ 6.752209] simplefb_detach_genpds+0x58/0x220
[ 6.752213] devm_action_release+0x50/0x98
[ 6.752216] release_nodes+0xd0/0x2c8
[ 6.752219] devres_release_all+0xfc/0x178
[ 6.752221] device_unbind_cleanup+0x28/0x168
[ 6.752224] device_release_driver_internal+0x34c/0x470
[ 6.752228] device_release_driver+0x20/0x38
[ 6.752231] bus_remove_device+0x1b0/0x380
[ 6.752234] device_del+0x314/0x820
[ 6.752238] platform_device_del+0x3c/0x1e8
[ 6.752242] platform_device_unregister+0x20/0x50
[ 6.752246] aperture_detach_platform_device+0x1c/0x30
[ 6.752250] aperture_detach_devices+0x16c/0x290
[ 6.752253] aperture_remove_conflicting_devices+0x34/0x50
...
[ 6.752343]
[ 6.967409] Allocated by task 62:
[ 6.970724] kasan_save_stack+0x3c/0x70
[ 6.974560] kasan_save_track+0x20/0x40
[ 6.978397] kasan_save_alloc_info+0x40/0x58
[ 6.982670] __kasan_kmalloc+0xd4/0xd8
[ 6.986420] __kmalloc_noprof+0x194/0x540
[ 6.990432] framebuffer_alloc+0xc8/0x130
[ 6.994444] simplefb_probe+0x258/0x2378
...
[ 7.054356]
[ 7.055838] Freed by task 227:
[ 7.058891] kasan_save_stack+0x3c/0x70
[ 7.062727] kasan_save_track+0x20/0x40
[ 7.066565] kasan_save_free_info+0x4c/0x80
[ 7.070751] __kasan_slab_free+0x6c/0xa0
[ 7.074675] kfree+0x10c/0x380
[ 7.077727] framebuffer_release+0x5c/0x90
[ 7.081826] simplefb_destroy+0x1b4/0x2c0
[ 7.085837] put_fb_info+0x98/0x100
[ 7.089326] unregister_framebuffer+0x178/0x320
[ 7.093861] simplefb_remove+0x3c/0x60
[ 7.097611] platform_remove+0x60/0x98
[ 7.101361] device_remove+0xb8/0x160
[ 7.105024] device_release_driver_internal+0x2fc/0x470
[ 7.110256] device_release_driver+0x20/0x38
[ 7.114529] bus_remove_device+0x1b0/0x380
[ 7.118628] device_del+0x314/0x820
[ 7.122116] platform_device_del+0x3c/0x1e8
[ 7.126302] platform_device_unregister+0x20/0x50
[ 7.131012] aperture_detach_platform_device+0x1c/0x30
[ 7.136157] aperture_detach_devices+0x16c/0x290
[ 7.140779] aperture_remove_conflicting_devices+0x34/0x50
...

Reported-by: Daniel Huhardeaux <tech@tootai.net>
Cc: stable@vger.kernel.org
Fixes: 92a511a568e44 ("fbdev/simplefb: Add support for generic power-domains")
Signed-off-by: Janne Grunau <j@jannau.net>
Reviewed-by: Hans de Goede <hansg@kernel.org>
Signed-off-by: Helge Deller <deller@gmx.de>

authored by

Janne Grunau and committed by
Helge Deller
da1bb913 2e3da8cf

+23 -8
+23 -8
drivers/video/fbdev/simplefb.c
··· 93 93 94 94 static void simplefb_clocks_destroy(struct simplefb_par *par); 95 95 static void simplefb_regulators_destroy(struct simplefb_par *par); 96 + static void simplefb_detach_genpds(void *res); 96 97 97 98 /* 98 99 * fb_ops.fb_destroy is called by the last put_fb_info() call at the end ··· 106 105 107 106 simplefb_regulators_destroy(info->par); 108 107 simplefb_clocks_destroy(info->par); 108 + simplefb_detach_genpds(info->par); 109 109 if (info->screen_base) 110 110 iounmap(info->screen_base); 111 111 ··· 447 445 if (!IS_ERR_OR_NULL(par->genpds[i])) 448 446 dev_pm_domain_detach(par->genpds[i], true); 449 447 } 448 + par->num_genpds = 0; 450 449 } 451 450 452 451 static int simplefb_attach_genpds(struct simplefb_par *par, 453 452 struct platform_device *pdev) 454 453 { 455 454 struct device *dev = &pdev->dev; 456 - unsigned int i; 455 + unsigned int i, num_genpds; 457 456 int err; 458 457 459 458 err = of_count_phandle_with_args(dev->of_node, "power-domains", ··· 468 465 return err; 469 466 } 470 467 471 - par->num_genpds = err; 468 + num_genpds = err; 472 469 473 470 /* 474 471 * Single power-domain devices are handled by the driver core, so 475 472 * nothing to do here. 476 473 */ 477 - if (par->num_genpds <= 1) 474 + if (num_genpds <= 1) { 475 + par->num_genpds = num_genpds; 478 476 return 0; 477 + } 479 478 480 - par->genpds = devm_kcalloc(dev, par->num_genpds, sizeof(*par->genpds), 479 + par->genpds = devm_kcalloc(dev, num_genpds, sizeof(*par->genpds), 481 480 GFP_KERNEL); 482 481 if (!par->genpds) 483 482 return -ENOMEM; 484 483 485 - par->genpd_links = devm_kcalloc(dev, par->num_genpds, 484 + par->genpd_links = devm_kcalloc(dev, num_genpds, 486 485 sizeof(*par->genpd_links), 487 486 GFP_KERNEL); 488 487 if (!par->genpd_links) 489 488 return -ENOMEM; 489 + 490 + /* 491 + * Set par->num_genpds only after genpds and genpd_links are allocated 492 + * to exit early from simplefb_detach_genpds() without full 493 + * initialisation. 494 + */ 495 + par->num_genpds = num_genpds; 490 496 491 497 for (i = 0; i < par->num_genpds; i++) { 492 498 par->genpds[i] = dev_pm_domain_attach_by_id(dev, i); ··· 518 506 dev_warn(dev, "failed to link power-domain %u\n", i); 519 507 } 520 508 521 - return devm_add_action_or_reset(dev, simplefb_detach_genpds, par); 509 + return 0; 522 510 } 523 511 #else 512 + static void simplefb_detach_genpds(void *res) { } 524 513 static int simplefb_attach_genpds(struct simplefb_par *par, 525 514 struct platform_device *pdev) 526 515 { ··· 635 622 ret = devm_aperture_acquire_for_platform_device(pdev, par->base, par->size); 636 623 if (ret) { 637 624 dev_err(&pdev->dev, "Unable to acquire aperture: %d\n", ret); 638 - goto error_regulators; 625 + goto error_genpds; 639 626 } 640 627 ret = register_framebuffer(info); 641 628 if (ret < 0) { 642 629 dev_err(&pdev->dev, "Unable to register simplefb: %d\n", ret); 643 - goto error_regulators; 630 + goto error_genpds; 644 631 } 645 632 646 633 dev_info(&pdev->dev, "fb%d: simplefb registered!\n", info->node); 647 634 648 635 return 0; 649 636 637 + error_genpds: 638 + simplefb_detach_genpds(par); 650 639 error_regulators: 651 640 simplefb_regulators_destroy(par); 652 641 error_clocks: