diff options
author | Hans de Goede <hdegoede@redhat.com> | 2010-12-30 17:54:33 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-01-19 08:44:54 -0500 |
commit | 27074efa2ee8c1ef07dc5f644104e35d39e43322 (patch) | |
tree | 2ba18e26633ad557b37cc3b42c1cd1381c069d61 | |
parent | 4e770f7602fb2285f3106f98109d0685618ddc22 (diff) |
[media] gspca_main: Locking fixes 2
Before this patch vidioc_dqbuf is using its own read_lock, where as
other queue related functions use queue_lock. This means that dqbuf is
accessing several variables in a racy manor. The most important one
being fr_o, which may be changed from underneath dqbuf by vidioc_reqbufs
or vidioc_streamoff. Other variables which it accesses unprotected
are gspca_dev->memory, gspca_dev->streaming and gspca_dev->capt_file.
This patch fixes this by changing vidioc_dqbuf to also use the queue_lock.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Jean-Francois Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/gspca/gspca.c | 112 | ||||
-rw-r--r-- | drivers/media/video/gspca/gspca.h | 1 |
2 files changed, 58 insertions, 55 deletions
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 74626b6a9eb4..64ecf669d655 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c | |||
@@ -1831,33 +1831,77 @@ out: | |||
1831 | return ret; | 1831 | return ret; |
1832 | } | 1832 | } |
1833 | 1833 | ||
1834 | static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file, | ||
1835 | enum v4l2_memory memory) | ||
1836 | { | ||
1837 | if (!gspca_dev->present) | ||
1838 | return -ENODEV; | ||
1839 | if (gspca_dev->capt_file != file || gspca_dev->memory != memory || | ||
1840 | !gspca_dev->streaming) | ||
1841 | return -EINVAL; | ||
1842 | |||
1843 | /* check if a frame is ready */ | ||
1844 | return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i); | ||
1845 | } | ||
1846 | |||
1847 | static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, | ||
1848 | enum v4l2_memory memory) | ||
1849 | { | ||
1850 | int ret; | ||
1851 | |||
1852 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | ||
1853 | return -ERESTARTSYS; | ||
1854 | ret = frame_ready_nolock(gspca_dev, file, memory); | ||
1855 | mutex_unlock(&gspca_dev->queue_lock); | ||
1856 | return ret; | ||
1857 | } | ||
1858 | |||
1834 | /* | 1859 | /* |
1835 | * wait for a video frame | 1860 | * dequeue a video buffer |
1836 | * | 1861 | * |
1837 | * If a frame is ready, its index is returned. | 1862 | * If nonblock_ing is false, block until a buffer is available. |
1838 | */ | 1863 | */ |
1839 | static int frame_wait(struct gspca_dev *gspca_dev, | 1864 | static int vidioc_dqbuf(struct file *file, void *priv, |
1840 | int nonblock_ing) | 1865 | struct v4l2_buffer *v4l2_buf) |
1841 | { | 1866 | { |
1842 | int i, ret; | 1867 | struct gspca_dev *gspca_dev = priv; |
1868 | struct gspca_frame *frame; | ||
1869 | int i, j, ret; | ||
1843 | 1870 | ||
1844 | /* check if a frame is ready */ | 1871 | PDEBUG(D_FRAM, "dqbuf"); |
1845 | i = gspca_dev->fr_o; | 1872 | |
1846 | if (i == atomic_read(&gspca_dev->fr_i)) { | 1873 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
1847 | if (nonblock_ing) | 1874 | return -ERESTARTSYS; |
1875 | |||
1876 | for (;;) { | ||
1877 | ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory); | ||
1878 | if (ret < 0) | ||
1879 | goto out; | ||
1880 | if (ret > 0) | ||
1881 | break; | ||
1882 | |||
1883 | mutex_unlock(&gspca_dev->queue_lock); | ||
1884 | |||
1885 | if (file->f_flags & O_NONBLOCK) | ||
1848 | return -EAGAIN; | 1886 | return -EAGAIN; |
1849 | 1887 | ||
1850 | /* wait till a frame is ready */ | 1888 | /* wait till a frame is ready */ |
1851 | ret = wait_event_interruptible_timeout(gspca_dev->wq, | 1889 | ret = wait_event_interruptible_timeout(gspca_dev->wq, |
1852 | i != atomic_read(&gspca_dev->fr_i) || | 1890 | frame_ready(gspca_dev, file, v4l2_buf->memory), |
1853 | !gspca_dev->streaming || !gspca_dev->present, | ||
1854 | msecs_to_jiffies(3000)); | 1891 | msecs_to_jiffies(3000)); |
1855 | if (ret < 0) | 1892 | if (ret < 0) |
1856 | return ret; | 1893 | return ret; |
1857 | if (ret == 0 || !gspca_dev->streaming || !gspca_dev->present) | 1894 | if (ret == 0) |
1858 | return -EIO; | 1895 | return -EIO; |
1896 | |||
1897 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) | ||
1898 | return -ERESTARTSYS; | ||
1859 | } | 1899 | } |
1860 | 1900 | ||
1901 | i = gspca_dev->fr_o; | ||
1902 | j = gspca_dev->fr_queue[i]; | ||
1903 | frame = &gspca_dev->frame[j]; | ||
1904 | |||
1861 | gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; | 1905 | gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; |
1862 | 1906 | ||
1863 | if (gspca_dev->sd_desc->dq_callback) { | 1907 | if (gspca_dev->sd_desc->dq_callback) { |
@@ -1867,46 +1911,7 @@ static int frame_wait(struct gspca_dev *gspca_dev, | |||
1867 | gspca_dev->sd_desc->dq_callback(gspca_dev); | 1911 | gspca_dev->sd_desc->dq_callback(gspca_dev); |
1868 | mutex_unlock(&gspca_dev->usb_lock); | 1912 | mutex_unlock(&gspca_dev->usb_lock); |
1869 | } | 1913 | } |
1870 | return gspca_dev->fr_queue[i]; | ||
1871 | } | ||
1872 | |||
1873 | /* | ||
1874 | * dequeue a video buffer | ||
1875 | * | ||
1876 | * If nonblock_ing is false, block until a buffer is available. | ||
1877 | */ | ||
1878 | static int vidioc_dqbuf(struct file *file, void *priv, | ||
1879 | struct v4l2_buffer *v4l2_buf) | ||
1880 | { | ||
1881 | struct gspca_dev *gspca_dev = priv; | ||
1882 | struct gspca_frame *frame; | ||
1883 | int i, ret; | ||
1884 | |||
1885 | PDEBUG(D_FRAM, "dqbuf"); | ||
1886 | if (v4l2_buf->memory != gspca_dev->memory) | ||
1887 | return -EINVAL; | ||
1888 | 1914 | ||
1889 | if (!gspca_dev->present) | ||
1890 | return -ENODEV; | ||
1891 | |||
1892 | /* if not streaming, be sure the application will not loop forever */ | ||
1893 | if (!(file->f_flags & O_NONBLOCK) | ||
1894 | && !gspca_dev->streaming && gspca_dev->users == 1) | ||
1895 | return -EINVAL; | ||
1896 | |||
1897 | /* only the capturing file may dequeue */ | ||
1898 | if (gspca_dev->capt_file != file) | ||
1899 | return -EINVAL; | ||
1900 | |||
1901 | /* only one dequeue / read at a time */ | ||
1902 | if (mutex_lock_interruptible(&gspca_dev->read_lock)) | ||
1903 | return -ERESTARTSYS; | ||
1904 | |||
1905 | ret = frame_wait(gspca_dev, file->f_flags & O_NONBLOCK); | ||
1906 | if (ret < 0) | ||
1907 | goto out; | ||
1908 | i = ret; /* frame index */ | ||
1909 | frame = &gspca_dev->frame[i]; | ||
1910 | if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { | 1915 | if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { |
1911 | if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, | 1916 | if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, |
1912 | frame->data, | 1917 | frame->data, |
@@ -1919,10 +1924,10 @@ static int vidioc_dqbuf(struct file *file, void *priv, | |||
1919 | } | 1924 | } |
1920 | frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; | 1925 | frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; |
1921 | memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); | 1926 | memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); |
1922 | PDEBUG(D_FRAM, "dqbuf %d", i); | 1927 | PDEBUG(D_FRAM, "dqbuf %d", j); |
1923 | ret = 0; | 1928 | ret = 0; |
1924 | out: | 1929 | out: |
1925 | mutex_unlock(&gspca_dev->read_lock); | 1930 | mutex_unlock(&gspca_dev->queue_lock); |
1926 | return ret; | 1931 | return ret; |
1927 | } | 1932 | } |
1928 | 1933 | ||
@@ -2270,7 +2275,6 @@ int gspca_dev_probe2(struct usb_interface *intf, | |||
2270 | goto out; | 2275 | goto out; |
2271 | 2276 | ||
2272 | mutex_init(&gspca_dev->usb_lock); | 2277 | mutex_init(&gspca_dev->usb_lock); |
2273 | mutex_init(&gspca_dev->read_lock); | ||
2274 | mutex_init(&gspca_dev->queue_lock); | 2278 | mutex_init(&gspca_dev->queue_lock); |
2275 | init_waitqueue_head(&gspca_dev->wq); | 2279 | init_waitqueue_head(&gspca_dev->wq); |
2276 | 2280 | ||
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 97b77a26a2eb..a2a1a6aa0606 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h | |||
@@ -205,7 +205,6 @@ struct gspca_dev { | |||
205 | 205 | ||
206 | wait_queue_head_t wq; /* wait queue */ | 206 | wait_queue_head_t wq; /* wait queue */ |
207 | struct mutex usb_lock; /* usb exchange protection */ | 207 | struct mutex usb_lock; /* usb exchange protection */ |
208 | struct mutex read_lock; /* read protection */ | ||
209 | struct mutex queue_lock; /* ISOC queue protection */ | 208 | struct mutex queue_lock; /* ISOC queue protection */ |
210 | int usb_err; /* USB error - protected by usb_lock */ | 209 | int usb_err; /* USB error - protected by usb_lock */ |
211 | u16 pkt_size; /* ISOC packet size */ | 210 | u16 pkt_size; /* ISOC packet size */ |