Received: by 2002:a05:6a10:9848:0:0:0:0 with SMTP id x8csp1363020pxf; Fri, 9 Apr 2021 06:42:24 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwt/N/eIcVE5E6EeT7lgNsHvnfSNbclq7jTCQoijLmZBH+SynkxoC6Ai2DBmOrKiMXpUol7 X-Received: by 2002:a17:906:151a:: with SMTP id b26mr16303199ejd.492.1617975744143; Fri, 09 Apr 2021 06:42:24 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1617975744; cv=none; d=google.com; s=arc-20160816; b=GRbPyr267BaXQ5drKt+2JS5dFThKdpIP6VcxnnJdhsN47J/r7bA6Sdk0IG2Z/PDFGW RWjnf8F0flTA3Wfs41+ru7JbenNuw4xDRzG/6e6+3WPs+Fc8Fqe3OZuMsIu0/ig6ucXc kUumIikh2yVDjuKLpwyrQIHwMsoqUuLe8MkBJlCZLVYAU8t4/hF9x3ZKQ/9vkJt5netF C/Zz+ouaOyOEo98Fqv3ljSMwyiAVFVv/wabfRsAv2TFmttWX4j2uNTmdso1TEz+cczF5 vgGztc5eypDKrHR9MD1BRLj8O+AlFVMONH5VfA0POQLN+EDWKlSIro3kFdwXmNloPhFw oMsw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:content-language :in-reply-to:mime-version:user-agent:date:message-id:from:references :cc:to:subject:dkim-signature; bh=WyAXkHyeYFDUhsLzYM8bDFLDXw2T00DSRWM3uRuqBCo=; b=qRam/FUBQ82tLAehoaryVTq1ijfDHxNIkT+YYue0vispn/xVmxCfM86fJgDszaMmJN bMHYqoFCLftGD6yOHhS+1/OcliJ8EWdTJga/L0W1R3hLxUwmzn1ikZHR3wQpL68oGNqg rErKgWzX3aVjGWjvBvhZ8/TdHI8RN27koiKc5umNy49w5IwflvBdc8C4ZW4Ero+PWC10 /0ZZv+sKJtSVA5AmwhKXQy1Ye253lnZWLMYN+kcwau20UTWSnJWarMShMaNNEwRqt5YH I4i1stywrN1LsUbnpdrUERDWGItU2YN95fOfVWyURW2jTtIXfbAs480FYYvBsM7Oj4My beIA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=Jl28wbkl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id v23si1876260ejb.619.2021.04.09.06.42.00; Fri, 09 Apr 2021 06:42:24 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=Jl28wbkl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233051AbhDINkr (ORCPT + 99 others); Fri, 9 Apr 2021 09:40:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45914 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231621AbhDINkq (ORCPT ); Fri, 9 Apr 2021 09:40:46 -0400 Received: from mail-qk1-x731.google.com (mail-qk1-x731.google.com [IPv6:2607:f8b0:4864:20::731]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A9F71C061760; Fri, 9 Apr 2021 06:40:32 -0700 (PDT) Received: by mail-qk1-x731.google.com with SMTP id z10so5758507qkz.13; Fri, 09 Apr 2021 06:40:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language:content-transfer-encoding; bh=WyAXkHyeYFDUhsLzYM8bDFLDXw2T00DSRWM3uRuqBCo=; b=Jl28wbkl3tKn/CJQhoTPTychRWBToLJdlv2p4mv90xig3OLD+iOuhg0XKLkfbikUTV mRWrsSTst4SfjShUI7ZwDyNZz4mULo/GZI2O3g5aVrOY52651yBFoXoViF9uAhlXLiHQ 52z41TWUkoiRe3q7PClH2QsiCrV93ogK8G2OUlbTgyCQxKJrYWLssCBgrqiJQHNG9hH9 xvXUr718m5rKFdPRZ3GyL+NID/QZPowdYihp2z7Zl7yJh31eUIDPVcS9ChIZDwdgtsEj qu3IjuFKz2MwhfHeoqpxWu1gH5hOCalBR3rGf6hHavX3V5MF9kz0NSY0y1FqOsm3zsfv BKQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=WyAXkHyeYFDUhsLzYM8bDFLDXw2T00DSRWM3uRuqBCo=; b=t/eNO6uT2tnLFy5QJ0rsWmaEXLojWUEtDwiC0Y0FPaDCivWiq+36g14YYoSjYGSx3C kKqYSgH7osvH29vWF8XmduABYYHdhjW8aHGO3N8bbL3YP8g+0kuM8XFpAXwfRuXSkwu6 /EaZ2sFWsIfZBAEpg4GQ2Jy7K8cAhyDrQl0i9gVf3udO2pl9v9uLyTX6u5pNyvflJzzW 7msPh037FriYgz0SV7q/FKX0R4b+cVg0/Ffm3U+exsrdz0MaPRg6US05+5YB1na3czAV U4IIFI3MY2S22bY9k6DldBKiJls/L1K+gXk6dEFzN/byHEQWmnVmGFhlIajvrrS+r7SE kbvA== X-Gm-Message-State: AOAM532R01uNKedSPjBgQTDuZhvCJv60zlauZFmaB2Dz6uQou3JwCebf 3pDYANIKgIr2HaHJjWWR/cV6wpNxZfU= X-Received: by 2002:a37:a2c8:: with SMTP id l191mr13989448qke.413.1617975631464; Fri, 09 Apr 2021 06:40:31 -0700 (PDT) Received: from [192.168.15.5] ([189.120.76.30]) by smtp.gmail.com with ESMTPSA id o30sm1865448qtl.8.2021.04.09.06.40.29 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 09 Apr 2021 06:40:30 -0700 (PDT) Subject: Re: [PATCH] media: em28xx: Fix race condition between open and init function To: Shuah Khan , mchehab@kernel.org Cc: syzbot+b2391895514ed9ef4a8e@syzkaller.appspotmail.com, linux-media@vger.kernel.org, linux-kernel-mentees@lists.linuxfoundation.org, linux-kernel@vger.kernel.org References: <20210408121041.6655-1-igormtorrente@gmail.com> <5dd8a1f8-51f9-c4a9-e6e2-cc06e5615d01@linuxfoundation.org> From: Igor Torrente Message-ID: Date: Fri, 9 Apr 2021 10:40:27 -0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.9.0 MIME-Version: 1.0 In-Reply-To: <5dd8a1f8-51f9-c4a9-e6e2-cc06e5615d01@linuxfoundation.org> Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 4/8/21 2:37 PM, Shuah Khan wrote: > On 4/8/21 6:10 AM, Igor Matheus Andrade Torrente wrote: >> Fixes a race condition - for lack of a more precise term - between >> em28xx_v4l2_open and em28xx_v4l2_init, by detaching the v4l2_dev, >> media_pad and vdev structs from the em28xx_v4l2, and managing the >> lifetime of those objects more dynamicaly. >> >> The race happens when a thread[1] - containing the em28xx_v4l2_init() >> code - calls the v4l2_mc_create_media_graph(), and it return a error, >> if a thread[2] - running v4l2_open() - pass the verification point >> and reaches the em28xx_v4l2_open() before the thread[1] finishes >> the v4l2 subsystem deregistration, thread[1] will free all resources >> before the em28xx_v4l2_open() can process their things, >> because the em28xx_v4l2_init() has the dev->lock. And all this lead >> the thread[2] to cause a user-after-free. >> > > Have you tried this patch with em28xx device? You will have to take into > account the dependencies between the subdevs using the v4l2_dev. > No, I didn't because I don't have the device. I was using the syzbot .c that produces this use-after-free. I will try to modify the .c to test a complete initialization. Actually, I forgot to put RFC alongside [PATCH]. > Also try rmmod invidual drivers - what happens if you were to rmmod a > subdev driver? With v4l2_dev is not embedded in v4l2, this could open > up memory leaks or user-after-frees. > OK. Let me see what I can do. >> Reported-and-tested-by: >> syzbot+b2391895514ed9ef4a8e@syzkaller.appspotmail.com >> Signed-off-by: Igor Matheus Andrade Torrente >> --- >>   drivers/media/usb/em28xx/em28xx-camera.c |   4 +- >>   drivers/media/usb/em28xx/em28xx-video.c  | 188 ++++++++++++++--------- >>   drivers/media/usb/em28xx/em28xx.h        |   6 +- >>   3 files changed, 123 insertions(+), 75 deletions(-) >> >> diff --git a/drivers/media/usb/em28xx/em28xx-camera.c >> b/drivers/media/usb/em28xx/em28xx-camera.c >> index d1e66b503f4d..436c5a8cbbb6 100644 >> --- a/drivers/media/usb/em28xx/em28xx-camera.c >> +++ b/drivers/media/usb/em28xx/em28xx-camera.c >> @@ -340,7 +340,7 @@ int em28xx_init_camera(struct em28xx *dev) >>           v4l2->sensor_xtal = 4300000; >>           pdata.xtal = v4l2->sensor_xtal; >>           if (NULL == >> -            v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap, >> +            v4l2_i2c_new_subdev_board(v4l2->v4l2_dev, adap, >>                             &mt9v011_info, NULL)) >>               return -ENODEV; >>           v4l2->vinmode = EM28XX_VINMODE_RGB8_GRBG; >> @@ -394,7 +394,7 @@ int em28xx_init_camera(struct em28xx *dev) >>           v4l2->sensor_yres = 480; >>           subdev = >> -             v4l2_i2c_new_subdev_board(&v4l2->v4l2_dev, adap, >> +             v4l2_i2c_new_subdev_board(v4l2->v4l2_dev, adap, >>                              &ov2640_info, NULL); >>           if (!subdev) >>               return -ENODEV; >> diff --git a/drivers/media/usb/em28xx/em28xx-video.c >> b/drivers/media/usb/em28xx/em28xx-video.c >> index 6b84c3413e83..e1febb2bf06b 100644 >> --- a/drivers/media/usb/em28xx/em28xx-video.c >> +++ b/drivers/media/usb/em28xx/em28xx-video.c >> @@ -184,7 +184,7 @@ static int em28xx_vbi_supported(struct em28xx *dev) >>    */ >>   static void em28xx_wake_i2c(struct em28xx *dev) >>   { >> -    struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; >> +    struct v4l2_device *v4l2_dev = dev->v4l2->v4l2_dev; >>       v4l2_device_call_all(v4l2_dev, 0, core,  reset, 0); >>       v4l2_device_call_all(v4l2_dev, 0, video, s_routing, >> @@ -974,9 +974,17 @@ static void em28xx_v4l2_create_entities(struct >> em28xx *dev) >>       struct em28xx_v4l2 *v4l2 = dev->v4l2; >>       int ret, i; >> +    v4l2->video_pad = kzalloc(sizeof(*v4l2->video_pad), GFP_KERNEL); >> +    if (!v4l2->video_pad) { >> +        dev_err(&dev->intf->dev, >> +            "failed to allocate video pad memory!\n"); >> +        v4l2->vdev->entity.num_pads = 0; >> +        return; >> +    } >> + >>       /* Initialize Video, VBI and Radio pads */ >> -    v4l2->video_pad.flags = MEDIA_PAD_FL_SINK; >> -    ret = media_entity_pads_init(&v4l2->vdev.entity, 1, >> &v4l2->video_pad); >> +    v4l2->video_pad->flags = MEDIA_PAD_FL_SINK; >> +    ret = media_entity_pads_init(&v4l2->vdev->entity, 1, >> v4l2->video_pad); >>       if (ret < 0) >>           dev_err(&dev->intf->dev, >>               "failed to initialize video media entity!\n"); >> @@ -1132,11 +1140,11 @@ int em28xx_start_analog_streaming(struct >> vb2_queue *vq, unsigned int count) >>               f.type = V4L2_TUNER_RADIO; >>           else >>               f.type = V4L2_TUNER_ANALOG_TV; >> -        v4l2_device_call_all(&v4l2->v4l2_dev, >> +        v4l2_device_call_all(v4l2->v4l2_dev, >>                        0, tuner, s_frequency, &f); >>           /* Enable video stream at TV decoder */ >> -        v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 1); >> +        v4l2_device_call_all(v4l2->v4l2_dev, 0, video, s_stream, 1); >>       } >>       v4l2->streaming_users++; >> @@ -1157,7 +1165,7 @@ static void em28xx_stop_streaming(struct >> vb2_queue *vq) >>       if (v4l2->streaming_users-- == 1) { >>           /* Disable video stream at TV decoder */ >> -        v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 0); >> +        v4l2_device_call_all(v4l2->v4l2_dev, 0, video, s_stream, 0); >>           /* Last active user, so shutdown all the URBS */ >>           em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); >> @@ -1192,7 +1200,7 @@ void em28xx_stop_vbi_streaming(struct vb2_queue >> *vq) >>       if (v4l2->streaming_users-- == 1) { >>           /* Disable video stream at TV decoder */ >> -        v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_stream, 0); >> +        v4l2_device_call_all(v4l2->v4l2_dev, 0, video, s_stream, 0); >>           /* Last active user, so shutdown all the URBS */ >>           em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); >> @@ -1286,7 +1294,7 @@ static int em28xx_vb2_setup(struct em28xx *dev) >>   static void video_mux(struct em28xx *dev, int index) >>   { >> -    struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; >> +    struct v4l2_device *v4l2_dev = dev->v4l2->v4l2_dev; >>       dev->ctl_input = index; >>       dev->ctl_ainput = INPUT(index)->amux; >> @@ -1565,7 +1573,7 @@ static int vidioc_querystd(struct file *file, >> void *priv, v4l2_std_id *norm) >>   { >>       struct em28xx *dev = video_drvdata(file); >> -    v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, video, querystd, >> norm); >> +    v4l2_device_call_all(dev->v4l2->v4l2_dev, 0, video, querystd, norm); >>       return 0; >>   } >> @@ -1596,7 +1604,7 @@ static int vidioc_s_std(struct file *file, void >> *priv, v4l2_std_id norm) >>                 &v4l2->hscale, &v4l2->vscale); >>       em28xx_resolution_set(dev); >> -    v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); >> +    v4l2_device_call_all(v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); >>       return 0; >>   } >> @@ -1616,7 +1624,7 @@ static int vidioc_g_parm(struct file *file, void >> *priv, >>       p->parm.capture.readbuffers = EM28XX_MIN_BUF; >>       p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; >>       if (dev->is_webcam) { >> -        rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0, >> +        rc = v4l2_device_call_until_err(v4l2->v4l2_dev, 0, >>                           video, g_frame_interval, &ival); >>           if (!rc) >>               p->parm.capture.timeperframe = ival.interval; >> @@ -1648,7 +1656,7 @@ static int vidioc_s_parm(struct file *file, void >> *priv, >>       memset(&p->parm, 0, sizeof(p->parm)); >>       p->parm.capture.readbuffers = EM28XX_MIN_BUF; >>       p->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; >> -    rc = v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, 0, >> +    rc = v4l2_device_call_until_err(dev->v4l2->v4l2_dev, 0, >>                       video, s_frame_interval, &ival); >>       if (!rc) >>           p->parm.capture.timeperframe = ival.interval; >> @@ -1675,7 +1683,7 @@ static int vidioc_enum_input(struct file *file, >> void *priv, >>       if (INPUT(n)->type == EM28XX_VMUX_TELEVISION) >>           i->type = V4L2_INPUT_TYPE_TUNER; >> -    i->std = dev->v4l2->vdev.tvnorms; >> +    i->std = dev->v4l2->vdev->tvnorms; >>       /* webcams do not have the STD API */ >>       if (dev->is_webcam) >>           i->capabilities = 0; >> @@ -1839,7 +1847,7 @@ static int vidioc_g_tuner(struct file *file, >> void *priv, >>       strscpy(t->name, "Tuner", sizeof(t->name)); >> -    v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); >> +    v4l2_device_call_all(dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); >>       return 0; >>   } >> @@ -1851,7 +1859,7 @@ static int vidioc_s_tuner(struct file *file, >> void *priv, >>       if (t->index != 0) >>           return -EINVAL; >> -    v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); >> +    v4l2_device_call_all(dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); >>       return 0; >>   } >> @@ -1878,8 +1886,8 @@ static int vidioc_s_frequency(struct file *file, >> void *priv, >>       if (f->tuner != 0) >>           return -EINVAL; >> -    v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f); >> -    v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, >> &new_freq); >> +    v4l2_device_call_all(v4l2->v4l2_dev, 0, tuner, s_frequency, f); >> +    v4l2_device_call_all(v4l2->v4l2_dev, 0, tuner, g_frequency, >> &new_freq); >>       v4l2->frequency = new_freq.frequency; >>       return 0; >> @@ -1897,7 +1905,7 @@ static int vidioc_g_chip_info(struct file *file, >> void *priv, >>           strscpy(chip->name, "ac97", sizeof(chip->name)); >>       else >>           strscpy(chip->name, >> -            dev->v4l2->v4l2_dev.name, sizeof(chip->name)); >> +            dev->v4l2->v4l2_dev->name, sizeof(chip->name)); >>       return 0; >>   } >> @@ -2095,7 +2103,7 @@ static int radio_g_tuner(struct file *file, void >> *priv, >>       strscpy(t->name, "Radio", sizeof(t->name)); >> -    v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); >> +    v4l2_device_call_all(dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); >>       return 0; >>   } >> @@ -2108,7 +2116,7 @@ static int radio_s_tuner(struct file *file, void >> *priv, >>       if (t->index != 0) >>           return -EINVAL; >> -    v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); >> +    v4l2_device_call_all(dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); >>       return 0; >>   } >> @@ -2160,6 +2168,11 @@ static int em28xx_v4l2_open(struct file *filp) >>       if (mutex_lock_interruptible(&dev->lock)) >>           return -ERESTARTSYS; >> +    if (!dev->v4l2) { >> +        mutex_unlock(&dev->lock); >> +        return -ENODEV; >> +    } >> + >>       ret = v4l2_fh_open(filp); >>       if (ret) { >>           dev_err(&dev->intf->dev, >> @@ -2184,7 +2197,7 @@ static int em28xx_v4l2_open(struct file *filp) >>       if (vdev->vfl_type == VFL_TYPE_RADIO) { >>           em28xx_videodbg("video_open: setting radio device\n"); >> -        v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio); >> +        v4l2_device_call_all(v4l2->v4l2_dev, 0, tuner, s_radio); >>       } >>       kref_get(&dev->ref); >> @@ -2222,7 +2235,7 @@ static int em28xx_v4l2_fini(struct em28xx *dev) >>       mutex_lock(&dev->lock); >> -    v4l2_device_disconnect(&v4l2->v4l2_dev); >> +    v4l2_device_disconnect(v4l2->v4l2_dev); >>       em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); >> @@ -2238,14 +2251,15 @@ static int em28xx_v4l2_fini(struct em28xx *dev) >>                video_device_node_name(&v4l2->vbi_dev)); >>           video_unregister_device(&v4l2->vbi_dev); >>       } >> -    if (video_is_registered(&v4l2->vdev)) { >> +    if (video_is_registered(v4l2->vdev)) { >>           dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", >> -             video_device_node_name(&v4l2->vdev)); >> -        video_unregister_device(&v4l2->vdev); >> +             video_device_node_name(v4l2->vdev)); >> +        video_unregister_device(v4l2->vdev); >>       } >>       v4l2_ctrl_handler_free(&v4l2->ctrl_handler); >> -    v4l2_device_unregister(&v4l2->v4l2_dev); >> +    v4l2_device_unregister(v4l2->v4l2_dev); >> +    v4l2_device_put(v4l2->v4l2_dev); >>       kref_put(&v4l2->ref, em28xx_free_v4l2); >> @@ -2305,7 +2319,7 @@ static int em28xx_v4l2_close(struct file *filp) >>               goto exit; >>           /* Save some power by putting tuner to sleep */ >> -        v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby); >> +        v4l2_device_call_all(v4l2->v4l2_dev, 0, tuner, standby); >>           /* do this before setting alternate! */ >>           em28xx_set_mode(dev, EM28XX_SUSPEND); >> @@ -2330,6 +2344,17 @@ static int em28xx_v4l2_close(struct file *filp) >>       return 0; >>   } >> +void em28xx_vdev_release(struct video_device *vdev) >> +{ >> +#ifdef CONFIG_MEDIA_CONTROLLER >> +    int i; >> + >> +    for (i = 0; i < vdev->entity.num_pads; i++) >> +        kfree(&vdev->entity.pads[i]); >> +#endif >> +    kfree(vdev); >> +} >> + >>   static const struct v4l2_file_operations em28xx_v4l_fops = { >>       .owner         = THIS_MODULE, >>       .open          = em28xx_v4l2_open, >> @@ -2387,7 +2412,7 @@ static const struct v4l2_ioctl_ops >> video_ioctl_ops = { >>   static const struct video_device em28xx_video_template = { >>       .fops        = &em28xx_v4l_fops, >>       .ioctl_ops    = &video_ioctl_ops, >> -    .release    = video_device_release_empty, >> +    .release    = em28xx_vdev_release, >>       .tvnorms    = V4L2_STD_ALL, >>   }; >> @@ -2445,7 +2470,7 @@ static void em28xx_vdev_init(struct em28xx *dev, >>                    const char *type_name) >>   { >>       *vfd        = *template; >> -    vfd->v4l2_dev    = &dev->v4l2->v4l2_dev; >> +    vfd->v4l2_dev    = dev->v4l2->v4l2_dev; >>       vfd->lock    = &dev->lock; >>       if (dev->is_webcam) >>           vfd->tvnorms = 0; >> @@ -2459,7 +2484,7 @@ static void em28xx_vdev_init(struct em28xx *dev, >>   static void em28xx_tuner_setup(struct em28xx *dev, unsigned short >> tuner_addr) >>   { >>       struct em28xx_v4l2      *v4l2 = dev->v4l2; >> -    struct v4l2_device      *v4l2_dev = &v4l2->v4l2_dev; >> +    struct v4l2_device      *v4l2_dev = v4l2->v4l2_dev; >>       struct tuner_setup      tun_setup; >>       struct v4l2_frequency   f; >> @@ -2517,6 +2542,11 @@ static void em28xx_tuner_setup(struct em28xx >> *dev, unsigned short tuner_addr) >>       v4l2_device_call_all(v4l2_dev, 0, tuner, s_frequency, &f); >>   } >> +void em28xx_v4l2_dev_release(struct v4l2_device *v4l2_dev) >> +{ >> +    kfree(v4l2_dev); >> +} >> + >>   static int em28xx_v4l2_init(struct em28xx *dev) >>   { >>       u8 val; >> @@ -2541,26 +2571,35 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>       v4l2 = kzalloc(sizeof(*v4l2), GFP_KERNEL); >>       if (!v4l2) { >> -        mutex_unlock(&dev->lock); >> -        return -ENOMEM; >> +        ret = -ENOMEM; >> +        goto v4l2_error; >>       } >> + >>       kref_init(&v4l2->ref); >>       v4l2->dev = dev; >>       dev->v4l2 = v4l2; >> +    v4l2->v4l2_dev = kzalloc(sizeof(*v4l2->v4l2_dev), GFP_KERNEL); >> +    if (!v4l2->v4l2_dev) { >> +        ret = -ENOMEM; >> +        goto v4l2_dev_error; >> +    } >> + >> +    v4l2->v4l2_dev->release = em28xx_v4l2_dev_release; >> + >>   #ifdef CONFIG_MEDIA_CONTROLLER >> -    v4l2->v4l2_dev.mdev = dev->media_dev; >> +    v4l2->v4l2_dev->mdev = dev->media_dev; >>   #endif >> -    ret = v4l2_device_register(&dev->intf->dev, &v4l2->v4l2_dev); >> +    ret = v4l2_device_register(&dev->intf->dev, v4l2->v4l2_dev); >>       if (ret < 0) { >>           dev_err(&dev->intf->dev, >>               "Call to v4l2_device_register() failed!\n"); >> -        goto err; >> +        goto v4l2_device_register_error; >>       } >>       hdl = &v4l2->ctrl_handler; >>       v4l2_ctrl_handler_init(hdl, 8); >> -    v4l2->v4l2_dev.ctrl_handler = hdl; >> +    v4l2->v4l2_dev->ctrl_handler = hdl; >>       if (dev->is_webcam) >>           v4l2->progressive = true; >> @@ -2575,22 +2614,22 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>       /* request some modules */ >>       if (dev->has_msp34xx) >> -        v4l2_i2c_new_subdev(&v4l2->v4l2_dev, >> +        v4l2_i2c_new_subdev(v4l2->v4l2_dev, >>                       &dev->i2c_adap[dev->def_i2c_bus], >>                       "msp3400", 0, msp3400_addrs); >>       if (dev->board.decoder == EM28XX_SAA711X) >> -        v4l2_i2c_new_subdev(&v4l2->v4l2_dev, >> +        v4l2_i2c_new_subdev(v4l2->v4l2_dev, >>                       &dev->i2c_adap[dev->def_i2c_bus], >>                       "saa7115_auto", 0, saa711x_addrs); >>       if (dev->board.decoder == EM28XX_TVP5150) >> -        v4l2_i2c_new_subdev(&v4l2->v4l2_dev, >> +        v4l2_i2c_new_subdev(v4l2->v4l2_dev, >>                       &dev->i2c_adap[dev->def_i2c_bus], >>                       "tvp5150", 0, tvp5150_addrs); >>       if (dev->board.adecoder == EM28XX_TVAUDIO) >> -        v4l2_i2c_new_subdev(&v4l2->v4l2_dev, >> +        v4l2_i2c_new_subdev(v4l2->v4l2_dev, >>                       &dev->i2c_adap[dev->def_i2c_bus], >>                       "tvaudio", dev->board.tvaudio_addr, NULL); >> @@ -2601,13 +2640,13 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>           int has_demod = (dev->board.tda9887_conf & TDA9887_PRESENT); >>           if (dev->board.radio.type) >> -            v4l2_i2c_new_subdev(&v4l2->v4l2_dev, >> +            v4l2_i2c_new_subdev(v4l2->v4l2_dev, >>                           &dev->i2c_adap[dev->def_i2c_bus], >>                           "tuner", dev->board.radio_addr, >>                           NULL); > > Add null check for v4l2_i2c_new_subdev() and error handling. It was okay > check error prior to this change to allocating v4l2_dev. Now this has > to be handled as a error leg. I will add to all bellow. > >>           if (has_demod) >> -            v4l2_i2c_new_subdev(&v4l2->v4l2_dev, >> +            v4l2_i2c_new_subdev(v4l2->v4l2_dev, >>                           &dev->i2c_adap[dev->def_i2c_bus], >>                           "tuner", 0, >>                           v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); > > Same here: > > Add null check for v4l2_i2c_new_subdev() and error handling. It was okay > check error prior to this change to allocating v4l2_dev. Now this has > to be handled as a error leg. > >> @@ -2616,7 +2655,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>                   has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; >>               struct v4l2_subdev *sd; >> -            sd = v4l2_i2c_new_subdev(&v4l2->v4l2_dev, >> +            sd = v4l2_i2c_new_subdev(v4l2->v4l2_dev, >>                            &dev->i2c_adap[dev->def_i2c_bus], >>                            "tuner", 0, >>                            v4l2_i2c_tuner_addrs(type)); >> @@ -2624,7 +2663,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>               if (sd) >>                   tuner_addr = v4l2_i2c_subdev_addr(sd); > > Add null check for v4l2_i2c_new_subdev() and error handling. It was okay > check error prior to this change to allocating v4l2_dev. Now this has > to be handled as a error leg. > >>           } else { >> -            v4l2_i2c_new_subdev(&v4l2->v4l2_dev, >> +            v4l2_i2c_new_subdev(v4l2->v4l2_dev, >>                           &dev->i2c_adap[dev->def_i2c_bus], >>                           "tuner", tuner_addr, NULL); > > Add null check for v4l2_i2c_new_subdev() and error handling. It was okay > check error prior to this change to allocating v4l2_dev. Now this has > to be handled as a error leg. > >>           } >> @@ -2686,7 +2725,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>       /* set default norm */ >>       v4l2->norm = V4L2_STD_PAL; >> -    v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); >> +    v4l2_device_call_all(v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); >>       v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT; >>       /* Analog specific initialization */ >> @@ -2756,40 +2795,45 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>           goto unregister_dev; >>       /* allocate and fill video video_device struct */ >> -    em28xx_vdev_init(dev, &v4l2->vdev, &em28xx_video_template, "video"); >> +    v4l2->vdev = kzalloc(sizeof(*v4l2->vdev), GFP_KERNEL); >> +    if (!v4l2->vdev) { >> +        ret = -ENOMEM; >> +        goto unregister_dev; >> +    } >> + >> +    em28xx_vdev_init(dev, v4l2->vdev, &em28xx_video_template, "video"); >>       mutex_init(&v4l2->vb_queue_lock); >>       mutex_init(&v4l2->vb_vbi_queue_lock); >> -    v4l2->vdev.queue = &v4l2->vb_vidq; >> -    v4l2->vdev.queue->lock = &v4l2->vb_queue_lock; >> -    v4l2->vdev.device_caps = V4L2_CAP_READWRITE | >> V4L2_CAP_VIDEO_CAPTURE | >> +    v4l2->vdev->queue = &v4l2->vb_vidq; >> +    v4l2->vdev->queue->lock = &v4l2->vb_queue_lock; >> +    v4l2->vdev->device_caps = V4L2_CAP_READWRITE | >> V4L2_CAP_VIDEO_CAPTURE | >>                    V4L2_CAP_STREAMING; >>       if (dev->int_audio_type != EM28XX_INT_AUDIO_NONE) >> -        v4l2->vdev.device_caps |= V4L2_CAP_AUDIO; >> +        v4l2->vdev->device_caps |= V4L2_CAP_AUDIO; >>       if (dev->tuner_type != TUNER_ABSENT) >> -        v4l2->vdev.device_caps |= V4L2_CAP_TUNER; >> - >> +        v4l2->vdev->device_caps |= V4L2_CAP_TUNER; >>       /* disable inapplicable ioctls */ >>       if (dev->is_webcam) { >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_QUERYSTD); >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_STD); >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_STD); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_QUERYSTD); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_STD); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_STD); >>       } else { >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_PARM); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM); >>       } >>       if (dev->tuner_type == TUNER_ABSENT) { >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_TUNER); >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_TUNER); >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_FREQUENCY); >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_FREQUENCY); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_TUNER); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_TUNER); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_FREQUENCY); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_FREQUENCY); >>       } >>       if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) { >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_G_AUDIO); >> -        v4l2_disable_ioctl(&v4l2->vdev, VIDIOC_S_AUDIO); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_AUDIO); >> +        v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_AUDIO); >>       } >>       /* register v4l2 video video_device */ >> -    ret = video_register_device(&v4l2->vdev, VFL_TYPE_VIDEO, >> +    ret = video_register_device(v4l2->vdev, VFL_TYPE_VIDEO, >>                       video_nr[dev->devno]); >>       if (ret) { >>           dev_err(&dev->intf->dev, >> @@ -2863,7 +2907,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>       dev_info(&dev->intf->dev, >>            "V4L2 video device registered as %s\n", >> -         video_device_node_name(&v4l2->vdev)); >> +         video_device_node_name(v4l2->vdev)); >>       if (video_is_registered(&v4l2->vbi_dev)) >>           dev_info(&dev->intf->dev, >> @@ -2871,7 +2915,7 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>                video_device_node_name(&v4l2->vbi_dev)); >>       /* Save some power by putting tuner to sleep */ >> -    v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, standby); >> +    v4l2_device_call_all(v4l2->v4l2_dev, 0, tuner, standby); >>       /* initialize videobuf2 stuff */ >>       em28xx_vb2_setup(dev); >> @@ -2897,18 +2941,22 @@ static int em28xx_v4l2_init(struct em28xx *dev) >>                video_device_node_name(&v4l2->vbi_dev)); >>           video_unregister_device(&v4l2->vbi_dev); >>       } >> -    if (video_is_registered(&v4l2->vdev)) { >> +    if (video_is_registered(v4l2->vdev)) { >>           dev_info(&dev->intf->dev, >>                "V4L2 device %s deregistered\n", >> -             video_device_node_name(&v4l2->vdev)); >> -        video_unregister_device(&v4l2->vdev); >> +             video_device_node_name(v4l2->vdev)); >> +        video_unregister_device(v4l2->vdev); >>       } >>       v4l2_ctrl_handler_free(&v4l2->ctrl_handler); >> -    v4l2_device_unregister(&v4l2->v4l2_dev); >> -err: >> +    v4l2_device_unregister(v4l2->v4l2_dev); >> + >> +v4l2_device_register_error: >> +    v4l2_device_put(v4l2->v4l2_dev); >> +v4l2_dev_error: >>       dev->v4l2 = NULL; >>       kref_put(&v4l2->ref, em28xx_free_v4l2); >> +v4l2_error: >>       mutex_unlock(&dev->lock); >>       return ret; >>   } >> diff --git a/drivers/media/usb/em28xx/em28xx.h >> b/drivers/media/usb/em28xx/em28xx.h >> index 6648e11f1271..dbcc297b5a0d 100644 >> --- a/drivers/media/usb/em28xx/em28xx.h >> +++ b/drivers/media/usb/em28xx/em28xx.h >> @@ -552,10 +552,10 @@ struct em28xx_v4l2 { >>       struct kref ref; >>       struct em28xx *dev; >> -    struct v4l2_device v4l2_dev; >> +    struct v4l2_device *v4l2_dev; >>       struct v4l2_ctrl_handler ctrl_handler; >> -    struct video_device vdev; >> +    struct video_device *vdev; >>       struct video_device vbi_dev; >>       struct video_device radio_dev; >> @@ -601,7 +601,7 @@ struct em28xx_v4l2 { >>       unsigned int field_count; >>   #ifdef CONFIG_MEDIA_CONTROLLER >> -    struct media_pad video_pad, vbi_pad; >> +    struct media_pad *video_pad, vbi_pad; >>       struct media_entity *decoder; >>   #endif >>   }; >> > > thanks, > -- Shuah Thanks, --- Igor Matheus Andrade Torrente