From 92d354cbe95fcaa0e5f795ad7706bc8393915c57 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Fri, 23 Oct 2015 10:56:09 +0100 Subject: [PATCH] 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 Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers.c | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index aae94815b593..b63dd2ef78b5 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;