From: Javier Martinez Canillas Date: Tue, 3 May 2016 20:27:18 +0000 (-0400) Subject: s5p-mfc: Fix race between s5p_mfc_probe() and s5p_mfc_open() X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=c974c436eaf406225b7f4c5d27ae033a12667a38;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git s5p-mfc: Fix race between s5p_mfc_probe() and s5p_mfc_open() The s5p_mfc_probe() function registers the video devices before all the resources needed by s5p_mfc_open() are correctly initalized. So if s5p_mfc_open() function is called before s5p_mfc_probe() finishes (since the video dev is already registered), a NULL pointer dereference will happen due s5p_mfc_open() accessing uninitialized vars such as the struct s5p_mfc_dev .watchdog_timer and .mfc_ops fields. An example is following BUG caused by add_timer() getting a NULL pointer: [ 45.765374] kernel BUG at kernel/time/timer.c:790! [ 45.765381] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM ... [ 45.766149] [] (mod_timer) from [] (s5p_mfc_open+0x274/0x4d4 [s5p_mfc]) [ 45.766416] [] (s5p_mfc_open [s5p_mfc]) from [] (v4l2_open+0x9c/0x100 [videodev]) [ 45.766547] [] (v4l2_open [videodev]) from [] (chrdev_open+0x9c/0x178) [ 45.766575] [] (chrdev_open) from [] (do_dentry_open+0x1e0/0x300) [ 45.766595] [] (do_dentry_open) from [] (path_openat+0x800/0x10d4) [ 45.766610] [] (path_openat) from [] (do_filp_open+0x5c/0xc0) [ 45.766624] [] (do_filp_open) from [] (do_sys_open+0x10c/0x1bc) [ 45.766642] [] (do_sys_open) from [] (ret_fast_syscall+0x0/0x3c) [ 45.766655] Code: eaffffe3 e3a00001 e28dd008 e8bd81f0 (e7f001f2) Fix it by registering the video devs as the last step in s5p_mfc_probe(). Signed-off-by: Javier Martinez Canillas Tested-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki --- diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index beb4fd5bd326..501d822cad6b 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1212,14 +1212,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_dec_reg; - } - v4l2_info(&dev->v4l2_dev, - "decoder registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); /* encoder */ @@ -1237,14 +1229,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->vfl_dir = VFL_DIR_M2M; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - video_device_release(vfd); - goto err_enc_reg; - } - v4l2_info(&dev->v4l2_dev, - "encoder registered as /dev/video%d\n", vfd->num); video_set_drvdata(vfd, dev); platform_set_drvdata(pdev, dev); @@ -1261,15 +1245,34 @@ static int s5p_mfc_probe(struct platform_device *pdev) s5p_mfc_init_hw_cmds(dev); s5p_mfc_init_regs(dev); + /* Register decoder and encoder */ + ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + video_device_release(dev->vfd_dec); + goto err_dec_reg; + } + v4l2_info(&dev->v4l2_dev, + "decoder registered as /dev/video%d\n", dev->vfd_dec->num); + + ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0); + if (ret) { + v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); + video_device_release(dev->vfd_enc); + goto err_enc_reg; + } + v4l2_info(&dev->v4l2_dev, + "encoder registered as /dev/video%d\n", dev->vfd_enc->num); + pr_debug("%s--\n", __func__); return 0; /* Deinit MFC if probe had failed */ err_enc_reg: - video_device_release(dev->vfd_enc); -err_enc_alloc: video_unregister_device(dev->vfd_dec); err_dec_reg: + video_device_release(dev->vfd_enc); +err_enc_alloc: video_device_release(dev->vfd_dec); err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev);