| From 926234f1b8434c4409aa4c53637aa3362ca07cea Mon Sep 17 00:00:00 2001 |
| From: Ian Abbott <abbotti@mev.co.uk> |
| Date: Fri, 17 Jul 2020 15:52:56 +0100 |
| Subject: staging: comedi: addi_apci_1564: check INSN_CONFIG_DIGITAL_TRIG shift |
| |
| From: Ian Abbott <abbotti@mev.co.uk> |
| |
| commit 926234f1b8434c4409aa4c53637aa3362ca07cea upstream. |
| |
| The `INSN_CONFIG` comedi instruction with sub-instruction code |
| `INSN_CONFIG_DIGITAL_TRIG` includes a base channel in `data[3]`. This is |
| used as a right shift amount for other bitmask values without being |
| checked. Shift amounts greater than or equal to 32 will result in |
| undefined behavior. Add code to deal with this. |
| |
| Fixes: 1e15687ea472 ("staging: comedi: addi_apci_1564: add Change-of-State interrupt subdevice and required functions") |
| Cc: <stable@vger.kernel.org> #3.17+ |
| Signed-off-by: Ian Abbott <abbotti@mev.co.uk> |
| Link: https://lore.kernel.org/r/20200717145257.112660-4-abbotti@mev.co.uk |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/staging/comedi/drivers/addi_apci_1564.c | 20 ++++++++++++++------ |
| 1 file changed, 14 insertions(+), 6 deletions(-) |
| |
| --- a/drivers/staging/comedi/drivers/addi_apci_1564.c |
| +++ b/drivers/staging/comedi/drivers/addi_apci_1564.c |
| @@ -340,14 +340,22 @@ static int apci1564_cos_insn_config(stru |
| unsigned int *data) |
| { |
| struct apci1564_private *devpriv = dev->private; |
| - unsigned int shift, oldmask; |
| + unsigned int shift, oldmask, himask, lomask; |
| |
| switch (data[0]) { |
| case INSN_CONFIG_DIGITAL_TRIG: |
| if (data[1] != 0) |
| return -EINVAL; |
| shift = data[3]; |
| - oldmask = (1U << shift) - 1; |
| + if (shift < 32) { |
| + oldmask = (1U << shift) - 1; |
| + himask = data[4] << shift; |
| + lomask = data[5] << shift; |
| + } else { |
| + oldmask = 0xffffffffu; |
| + himask = 0; |
| + lomask = 0; |
| + } |
| switch (data[2]) { |
| case COMEDI_DIGITAL_TRIG_DISABLE: |
| devpriv->ctrl = 0; |
| @@ -371,8 +379,8 @@ static int apci1564_cos_insn_config(stru |
| devpriv->mode2 &= oldmask; |
| } |
| /* configure specified channels */ |
| - devpriv->mode1 |= data[4] << shift; |
| - devpriv->mode2 |= data[5] << shift; |
| + devpriv->mode1 |= himask; |
| + devpriv->mode2 |= lomask; |
| break; |
| case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS: |
| if (devpriv->ctrl != (APCI1564_DI_IRQ_ENA | |
| @@ -389,8 +397,8 @@ static int apci1564_cos_insn_config(stru |
| devpriv->mode2 &= oldmask; |
| } |
| /* configure specified channels */ |
| - devpriv->mode1 |= data[4] << shift; |
| - devpriv->mode2 |= data[5] << shift; |
| + devpriv->mode1 |= himask; |
| + devpriv->mode2 |= lomask; |
| break; |
| default: |
| return -EINVAL; |