umlaeute / v4l2loopback

v4l2-loopback device
GNU General Public License v2.0
3.69k stars 523 forks source link

Allow multiple VIDIOC_ENUM_FRAMEINTERVALS calls #497

Closed stoeckmann closed 1 year ago

stoeckmann commented 2 years ago

Currently the code allows only one successful ioctl call for VIDIOC_ENUM_FRAMEINTERVALS. This is most likely introduced to stop enumerating loops in userland tools.

The checks should comply with vidioc_enum_framesizes instead, which properly address this through index.

stoeckmann commented 2 years ago

How to reproduce, basically copied from https://github.com/umlaeute/v4l2loopback/pull/493

#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <linux/videodev2.h>

#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

void hax0r_out(char* message, bool success, int err) {
    printf("%s: %s\n", message, success ? "\033[1;32msuccess\033[0m" : "\033[1;31mfailed\033[0m");
    if(!success) {
        printf("ERROR %d: %s\n", err, strerror(err));
        exit(1);
    }
}

static void loopback_init(int fdwr, int w, int h) {
    struct v4l2_capability vid_caps;
    struct v4l2_format vid_format;

    size_t linewidth = w * 2;
    size_t framesize = h * linewidth;

    int ret_code;

    ret_code = ioctl(fdwr, VIDIOC_QUERYCAP, &vid_caps);
    hax0r_out("Querying device capabilities", ret_code >= 0, errno);

    memset(&vid_format, 0, sizeof(vid_format));
    vid_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
    vid_format.fmt.pix.width = w;
    vid_format.fmt.pix.height = h;
    vid_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    vid_format.fmt.pix.sizeimage = framesize;
    vid_format.fmt.pix.field = V4L2_FIELD_NONE;
    vid_format.fmt.pix.bytesperline = linewidth;
    vid_format.fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;

    ret_code = ioctl(fdwr, VIDIOC_S_FMT, &vid_format);
    hax0r_out("Setting frame format", ret_code >= 0, errno);

    ret_code = ioctl(fdwr, VIDIOC_STREAMON, &vid_format.type);
    hax0r_out("Activate video streaming", ret_code >= 0, errno);
}

static void loopback_free(int fdwr) {
    const enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT;

    int ret_code = ioctl(fdwr, VIDIOC_STREAMOFF, &type);
    hax0r_out("Deactivate video streaming", ret_code >= 0, errno);
}

int main(int argc, char **argv) {
    char *dev;
    int fd;
    struct v4l2_frmivalenum val = { 0 };

    if (argc < 2) {
        hax0r_out("Checking arguments", false, EINVAL);
    }

    dev = argv[1];
    hax0r_out("Checking device filename", strlen(dev), EINVAL);

    fd = open(dev, O_RDWR | O_CLOEXEC);
    hax0r_out("Opening device", fd >= 0, errno);

    loopback_init(fd, 800, 600);

    /* Here be exploits … */
    val.width = 800;
    val.height = 600;
    hax0r_out("Enumerating frame intervals (1)", ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &val) == 0, EINVAL);
    hax0r_out("Enumerating frame intervals (2)", ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &val) == 0, EINVAL);

    loopback_free(fd);

    hax0r_out("Closing device", close(fd) >= 0, errno);
    return 0;
}
umlaeute commented 1 year ago

thanks for your contribution.

closing as obsolete.