[PATCH] staging: comedi: fix extreme case of comedi_nsamples_left()

Ian Abbott abbotti at mev.co.uk
Fri Oct 23 09:56:09 UTC 2015


`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 at mev.co.uk>
---
 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 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;
-- 
2.6.1



More information about the devel mailing list