Re: [PATCH] media: mtk-jpeg: fix use-after-free in release path due to uncancelled work
From: Icenowy Zheng
Date: Mon Jun 01 2026 - 03:05:44 EST
在 2026-03-04三的 03:19 +0000,Fan Wu写道:
> The mtk_jpeg_release() function frees the context structure (ctx)
> without
> first cancelling any pending or running work in ctx->jpeg_work. This
> creates a race window where the workqueue callback may still be
> accessing
> the context memory after it has been freed.
This patch leads to frequent kernel warning on my mt8188-geralt-ciri
device:
```
[ 42.493013] ------------[ cut here ]------------
[ 42.497645] WARNING: kernel/workqueue.c:4302 at
__flush_work+0x34c/0x380, CPU#2: wireplumber/1100
[ 42.506521] Modules linked in: snd_seq_dummy snd_hrtimer snd_seq
snd_seq_device aes_ce_ccm rfcomm hid_himax(OE) bnep mtk_vcodec_dec_hw
btusb memconsole_coreboot btmtk btrtl btbcm vpd_sysfs cbmem
nf_conntrack_netbios_ns memconsole framebuffer_coreboot btintel
nf_conntrack_broadcast nft_fib_inet bluetooth nft_fib_ipv4 nft_fib_ipv6
nft_fib mt7921e nft_reject_inet mt7921_common nf_reject_ipv4
nf_reject_ipv6 mt792x_lib nft_reject mt76_connac_lib mt76 nft_ct
mtk_jpeg mt8188_mt6359 snd_soc_hdmi_codec mtk_vcodec_dec airoha_eth
mt6359_accdet v4l2_vp9 airoha_npu mtk_jpeg_enc_hw mtk_vcodec_enc
snd_soc_mt6359 v4l2_h264 mac80211 nft_chain_nat mtk_vcodec_dbgfs
mtk_jpeg_dec_hw mtk_vcodec_common mtk_vpu cfg80211 ip6table_nat
mtk_mdp3 ip6table_mangle v4l2_mem2mem ip6table_raw ofpart
videobuf2_dma_contig dsa_core videobuf2_memops phylink
ip6table_security hsr cmdlinepart videobuf2_v4l2 videobuf2_common
iptable_nat nf_nat bridge videodev cros_ec_sensors spi_nor nf_conntrack
stp mc cros_ec_sensors_core llc mtd
[ 42.506764] industrialio_triggered_buffer kfifo_buf nf_defrag_ipv6
snd_sof_mt8186 rfkill mtk_adsp_common nf_defrag_ipv4 iptable_mangle
mtk_adsp_ipc snd_soc_es8326 snd_soc_max98390 iptable_raw
snd_sof_xtensa_dsp mediatek_cpufreq_hw iptable_security snd_sof_of
snd_soc_mt8188_afe snd_sof snd_soc_dmic snd_soc_mtk_common
snd_sof_utils mt6359_auxadc snd_soc_core snd_compress ac97_bus mtk_scp
mt6577_auxadc snd_pcm_dmaengine snd_pcm mtk_rpmsg lvts_thermal
nf_tables mtk_scp_ipi snd_timer industrialio rpmsg_core snd
ip6table_filter coreboot_table soundcore ip6_tables iptable_filter
ip_tables x_tables joydev mousedev qrtr tun sch_fq_codel fuse nfnetlink
onboard_usb_dev panel_himax_hx83102 cros_usbpd_charger cros_ec_hwmon
cros_ec_sysfs cros_ec_sensorhub cros_usbpd_notify sbs_battery
gpio_cros_ec hwmon cros_charge_control cros_ec_chardev
cros_usbpd_logger cros_ec_debugfs hid_google_hammer hid_multitouch
hid_vivaldi_common cros_ec_keyb i2c_cros_ec_tunnel hid_generic
led_class cros_ec_dev rtc_mt6397 mt6359_regulator
[ 42.594635] tpm_tis_i2c_cr50 xhci_mtk_hcd i2c_hid_of tpm_tis_spi
i2c_hid tpm_tis_core cros_ec_spi cros_ec clk_mt8188_vdo1
clk_mt8188_vdo0 cros_ec_proto mt6397 clk_mt8188_vpp0 clk_mt8188_vpp1
mtk_dp ghash_ce phy_mtk_dp drm_dp_aux_bus phy_mtk_tphy sm4 mediatek_drm
drm_dma_helper drm_display_helper cec clk_mt8188_ipe clk_mt8188_venc
rc_core clk_mt8188_vdec clk_mt8188_cam clk_mt8188_img clk_mt8188_ccu
clk_mt8188_wpe panfrost mtk_mmsys mtk_mutex governor_simpleondemand
phy_mtk_mipi_dsi_drv clk_mt8188_mfg mtk_cmdq_helper nvmem_mtk_efuse
gpu_sched mtk_smi spi_mtk_nor mtu3 clk_mt8188_imp_iic_wrap mtk_sd roles
mmc_hsq udc_core cqhci spmi_mtk_pmif i2c_mt65xx spmi_devres
pwm_mtk_disp spi_mt65xx clk_mt8188_peri_ao clk_mt8188_adsp_audio26m
clk_mt8188_infra_ao spmi clk_mt8188_apmixedsys mtk_pmic_wrap mtk_wdt
mtk_adsp_mailbox mtk_iommu clk_mt8188_topckgen mtk_cmdq_mailbox fixed
ramoops pwm_bl i2c_dev aes_neon_bs aes_neon_blk aes_ce_blk
[ 42.765118] CPU: 2 UID: 975 PID: 1100 Comm: wireplumber Tainted: G
W OE 7.0.10-aosc-main #1 PREEMPT(lazy)
[ 42.775983] Tainted: [W]=WARN, [O]=OOT_MODULE, [E]=UNSIGNED_MODULE
[ 42.782155] Hardware name: Google Ciri sku1 board (DT)
[ 42.787286] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS
BTYPE=--)
[ 42.794243] pc : __flush_work+0x34c/0x380
[ 42.798251] lr : __cancel_work_sync+0x90/0xe0
[ 42.802604] sp : ffff8000854f3980
[ 42.805912] x29: ffff8000854f39e0 x28: ffff0000c9530000 x27:
0000000000000000
[ 42.813048] x26: 0000000000000000 x25: 0000000000000000 x24:
0000000000000001
[ 42.820183] x23: ffffb0d11de2bd40 x22: ffff0000c98db6c0 x21:
0000000000000001
[ 42.827319] x20: 0000000000000000 x19: ffff0000c2679f20 x18:
ffff8000851ad068
[ 42.834454] x17: 0000000000000000 x16: 0000000000000000 x15:
0000000000000000
[ 42.841588] x14: 0000000000000000 x13: 0000000000000000 x12:
0000000000000000
[ 42.848723] x11: 0000000000000000 x10: 0000000000000000 x9 :
ffffb0d11993da28
[ 42.855858] x8 : 0000000000000000 x7 : 0000000000000000 x6 :
0000000000000000
[ 42.862992] x5 : 0000000000000000 x4 : 0000000000000000 x3 :
0000000000000000
[ 42.870126] x2 : 0000000000000000 x1 : 0000000000000000 x0 :
0000000000000000
[ 42.877262] Call trace:
[ 42.879704] __flush_work+0x34c/0x380 (P)
[ 42.883713] __cancel_work_sync+0x90/0xe0
[ 42.887719] cancel_work_sync+0x20/0x50
[ 42.891551] mtk_jpeg_release+0x38/0xb0 [mtk_jpeg]
[ 42.896348] v4l2_release+0x94/0x118 [videodev]
[ 42.900910] __fput+0xec/0x330
[ 42.903968] fput_close_sync+0x44/0x160
[ 42.907803] __arm64_sys_close+0x44/0xa0
[ 42.911723] invoke_syscall.constprop.0+0x60/0xf0
[ 42.916426] el0_svc_common.constprop.0+0x114/0x140
[ 42.921302] do_el0_svc+0x28/0x58
[ 42.924614] el0_svc+0x44/0x1e0
[ 42.927754] el0t_64_sync_handler+0xc0/0x108
[ 42.932019] el0t_64_sync+0x1b8/0x1c0
[ 42.935680] ---[ end trace 0000000000000000 ]---
```
Thanks,
Icenowy
>
> Race condition:
>
> CPU 0 (release) CPU 1 (workqueue)
> ---------------- ------------------
> close()
> mtk_jpeg_release()
> mtk_jpegenc_worker()
> ctx = work->data
> // accessing ctx
>
> kfree(ctx) // freed!
> access ctx // UAF!
>
> The work is queued via queue_work() during JPEG encode/decode
> operations
> (via mtk_jpeg_device_run). If the device is closed while work is
> pending
> or running, the work handler will access freed memory.
>
> Fix this by calling cancel_work_sync() BEFORE acquiring the mutex.
> This
> ordering is critical: if cancel_work_sync() is called after
> mutex_lock(),
> and the work handler also tries to acquire the same mutex, it would
> cause
> a deadlock.
>
> Note: The open error path does NOT need cancel_work_sync() because
> INIT_WORK() only initializes the work structure - it does not
> schedule
> it. Work is only scheduled later during ioctl operations.
>
> Fixes: 5fb1c2361e56 ("mtk-jpegenc: add jpeg encode worker interface")
> Signed-off-by: Fan Wu <fanwu01@xxxxxxxxxx>
> ---
> drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
> b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
> index c01124a349f6..8c684756d5fc 100644
> --- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
> +++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
> @@ -1202,6 +1202,7 @@ static int mtk_jpeg_release(struct file *file)
> struct mtk_jpeg_dev *jpeg = video_drvdata(file);
> struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file);
>
> + cancel_work_sync(&ctx->jpeg_work);
> mutex_lock(&jpeg->lock);
> v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
> v4l2_ctrl_handler_free(&ctx->ctrl_hdl);