diff options
author | Ian Abbott <abbotti@mev.co.uk> | 2015-10-23 09:56:09 (GMT) |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-10-27 05:31:25 (GMT) |
commit | 92d354cbe95fcaa0e5f795ad7706bc8393915c57 (patch) | |
tree | c157390ee302f6f530be5586b52e386c5a2e4a9d /drivers/staging/comedi/drivers.c | |
parent | b5c4e4e9d1b31e307d855d3cacbb6f3381ef6140 (diff) | |
download | linux-92d354cbe95fcaa0e5f795ad7706bc8393915c57.tar.xz |
staging: comedi: fix extreme case of comedi_nsamples_left()
`comedi_nsamples_left(s, nsamples)` returns the number of samples
remaining to complete an asynchronous command or the passed in
`nsamples`, whichever is lower. However, it goes wrong in the extreme
case of setting the `nsamples` parameter to `UINT_MAX` when the number
of conversions per "scan" (`s->async->cmd.scan_end_arg`) is 1. It uses
`comedi_nscans_remaining(s, nscans)` to determine the number of scans
remaining, or the parameter `nscans`, whichever is lower. To determine
the parameter `nscans`, it divides `nsamples` by the number of
conversions per scan and adds 1. The addition of 1 is to avoid setting
the parameter `nscans` to 0, as `comedi_nscans_remaining(s, nscans)`
treats that value specially. However in the extreme case where
`nsamples` is `UINT_MAX` and the number of samples per scan is 1, the
addition of 1 to `nscans` overflows, producing the unwanted 0.
Fix it by refactoring new a function `__comedi_nscans_remaining(s,
nscans)` out of `comedi_nscans_remaining(s, nscans)`. The new function
does everything except the special handling when `nscans` is 0. Change
`comedi_nsamples_remaining()` to call the new function without adding 1
to `nscans` to avoid the overflow.
This overflow bug doesn't affect any of the current COMEDI drivers. I
stumbled across it while changing to one of the drivers.
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/comedi/drivers.c')
-rw-r--r-- | drivers/staging/comedi/drivers.c | 38 |
1 files changed, 21 insertions, 17 deletions
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index aae9481..b63dd2e 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -425,6 +425,24 @@ unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s) } EXPORT_SYMBOL_GPL(comedi_bytes_per_scan); +static unsigned int __comedi_nscans_left(struct comedi_subdevice *s, + unsigned int nscans) +{ + struct comedi_async *async = s->async; + struct comedi_cmd *cmd = &async->cmd; + + if (cmd->stop_src == TRIG_COUNT) { + unsigned int scans_left = 0; + + if (async->scans_done < cmd->stop_arg) + scans_left = cmd->stop_arg - async->scans_done; + + if (nscans > scans_left) + nscans = scans_left; + } + return nscans; +} + /** * comedi_nscans_left() - Return the number of scans left in the command * @s: COMEDI subdevice. @@ -442,25 +460,12 @@ EXPORT_SYMBOL_GPL(comedi_bytes_per_scan); unsigned int comedi_nscans_left(struct comedi_subdevice *s, unsigned int nscans) { - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - if (nscans == 0) { unsigned int nbytes = comedi_buf_read_n_available(s); nscans = nbytes / comedi_bytes_per_scan(s); } - - if (cmd->stop_src == TRIG_COUNT) { - unsigned int scans_left = 0; - - if (async->scans_done < cmd->stop_arg) - scans_left = cmd->stop_arg - async->scans_done; - - if (nscans > scans_left) - nscans = scans_left; - } - return nscans; + return __comedi_nscans_left(s, nscans); } EXPORT_SYMBOL_GPL(comedi_nscans_left); @@ -479,9 +484,8 @@ unsigned int comedi_nsamples_left(struct comedi_subdevice *s, struct comedi_cmd *cmd = &async->cmd; if (cmd->stop_src == TRIG_COUNT) { - /* +1 to force comedi_nscans_left() to return the scans left */ - unsigned int nscans = (nsamples / cmd->scan_end_arg) + 1; - unsigned int scans_left = comedi_nscans_left(s, nscans); + unsigned int nscans = nsamples / cmd->scan_end_arg; + unsigned int scans_left = __comedi_nscans_left(s, nscans); unsigned int scan_pos = comedi_bytes_to_samples(s, async->scan_progress); unsigned long long samples_left = 0; |