gspcav2-0.0.28 locking audit results

Hans de Goede <[email protected]>
Newsgroups gmane.linux.drivers.spca50x.devel
Message-ID <[email protected]>
Hi Jef & All,

I've done a full audit of all locking in the gspcav2_main code, below is a 
summary of my findings, I'll be working on fixing all this the next couple of 
days as time permits, so expect a few big patches soonish.

In the mean time any feedback on my perceived problems, and suggested fixes it 
much welcome.

---

gspca_init_transfer
-> usb_lock interruptible
-> always called with queue_lock held


gspca_stream_off
-> usb_lock interruptible, no check, BAD!
-> always called with queue_lock held


vidioc_s_fmt_cap
-> queue_lock interruptible, no check, BAD!
-> if already streaming also takes usb lock through
    gspca_stream_off / gspca_init_transfer
-> stops / start the stream if already streaming, but in this case
    the frsz will most likely change, without reallocing the bufs, BAD!
    should return -EBUSY if there are already frames allocated (independent
    of streaming) instead.


dev_open
-> queue_lock + usb_lock, interruptible


dev_close
-> queue_lock + usb_lock through gspca_stream_off, interruptible, no check,
    BAD!
Then:
-> usb_lock, interruptible, no check, BAD!
Then:
-> queue_lock, interruptible, no check, BAD!


vidioc_s_ctrl
-> usb_lock, interruptible, no check, BAD!

vidioc_g_ctrl
-> usb_lock, interruptible, no check, BAD!
Note: the usb_lock isn't always needed here, should be left over to sd code


vidioc_reqbufs
-> queue_lock, interruptible
Note: only takes the lock after calculating the framesize, possible race
with vidioc_s_fmt_cap which can change the frame size


vidioc_querybuf
-> no locking
Should probably take the queuelock, as many queue operations can change the
buffers and since this does a memcpy of the bufferstate, this could cause a
race where it gets part of an old and parts of a new buffer state.


vidioc_streamon
-> queue_lock + usb_lock through gspca_init_transfer, interruptible


vidioc_streamoff
-> queue_lock + usb_lock through gspca_stream_off, interruptible, no check,
    BAD!


vidioc_g_jpegcomp
vidioc_s_jpegcomp
-> usb_lock, interruptible, no check, BAD!


vidioc_s_parm
-> usb_lock, why o why?, interruptible, no check, BAD!


dev_mmap
-> queue_lock, interruptible

dev_poll
-> queue_lock, interruptible


vidioc_dqbuf
-> read_lock, interruptible
BAD protects against multiple readers but not against other queue
manipilating functions such as stream on/off


vidioc_qbuf
-> queue_lock, interruptible
Note: lock should be moved up to also protect the checking for the nframes
and esp for checking if the memory types match.


dev_read
-> no locking, should have locking for checking nframes and streaming
(otherwise there could be potential races). Should perhaps also check
somehow if nframes > 0, if this were not allocated through other means
(application mixing read and mmap modes).


gspca_dev_probe
no locking, non needed, but should set gspca_dev->present to 1 before
registering the v4ldevice


gspca_disconnect
-calls gspca_kill_transfer which isn't needed as all urbs are killed on
  disconnect by the usb core, and if streaming the closing of the device will
  call gspca_kill_transfer freeing the urbs, does some locking surrounding
  gspca_kill_transfer, which is bad as its interruptible, but doesn't check
  returns
-possible race with open between the "while (gspca_dev->users != 0) { ... }
  and the "video_unregister_device(&gspca_dev->vdev);", yes the window is
  small but it is there. This can be fixed by:
  1) not waiting for users at all, instead make gspca_dev refcounted,
     ref up in open, down in close and disconnect, free if ref hits zero
  2) check gspca_dev->present everywhere
  3) disconnect now just becomes a set gspca_dev->present to 0,
     take and release all locks (to make sure everyone has seen that
     gspca_dev->present has become 0), deref and a unregister v4ldev.
  4) using a global mutex to protect the refcount from open/close/disconnect
     races, this must be global so that it can be checked in open while
     racing against disconnect, without checking a mutex in potentially freed
     memory, see the comments explaining this in my usbvideo2 "core" code
  Note: the above requires a v4l2_ioctl wrapper which checks for
  gspca_dev->present before calling v4l2_ioctl, otherwise v4l2_ioctl could be
  called on a unregistered v4ldev, making it rather unhappy! Again for all the
  details see my usbvideo2 "core" code.

  Fixing things this way has the added advantage that a quick disconnect /
  connect due to an usb glitch no longer changes the /dev/videoX number, as
  /dev/videoX gets deregistered immediately rather then waiting for any using
  apps to close it.


misc
-> a struct module * must be added to struct sd_desc, and then its
    usage count must be increased in dev_open, an decreased in dec_close,
    currently someone can rmmod gspca_driver, while /dev/videoX is open!


None lock related notes:
1) The framequeues should be cleared (all buffers back to user state) upon
    a stream off.

2) gspca_init_transfer should call gspca_stream_off rather then
    kill_transfert on error so that the stream also gets stopped at the cam
    side

Regards,

Hans


-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.