i40e: prevent crash on probe if hw registers have invalid values
authorMichal Schmidt <mschmidt@redhat.com>
Wed, 11 Oct 2023 23:33:32 +0000 (16:33 -0700)
committerJakub Kicinski <kuba@kernel.org>
Sat, 14 Oct 2023 00:57:05 +0000 (17:57 -0700)
The hardware provides the indexes of the first and the last available
queue and VF. From the indexes, the driver calculates the numbers of
queues and VFs. In theory, a faulty device might say the last index is
smaller than the first index. In that case, the driver's calculation
would underflow, it would attempt to write to non-existent registers
outside of the ioremapped range and crash.

I ran into this not by having a faulty device, but by an operator error.
I accidentally ran a QE test meant for i40e devices on an ice device.
The test used 'echo i40e > /sys/...ice PCI device.../driver_override',
bound the driver to the device and crashed in one of the wr32 calls in
i40e_clear_hw.

Add checks to prevent underflows in the calculations of num_queues and
num_vfs. With this fix, the wrong device probing reports errors and
returns a failure without crashing.

Fixes: 838d41d92a90 ("i40e: clear all queues and interrupts")
Signed-off-by: Michal Schmidt <mschmidt@redhat.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Tested-by: Pucha Himasekhar Reddy <himasekharx.reddy.pucha@intel.com> (A Contingent worker at Intel)
Link: https://lore.kernel.org/r/20231011233334.336092-2-jacob.e.keller@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/intel/i40e/i40e_common.c

index eeef20f77106255d3920abbc5feee38147211e27..1b493854f52292140866cf98c6346fe3787b2e59 100644 (file)
@@ -1082,7 +1082,7 @@ void i40e_clear_hw(struct i40e_hw *hw)
                     I40E_PFLAN_QALLOC_FIRSTQ_SHIFT;
        j = (val & I40E_PFLAN_QALLOC_LASTQ_MASK) >>
            I40E_PFLAN_QALLOC_LASTQ_SHIFT;
-       if (val & I40E_PFLAN_QALLOC_VALID_MASK)
+       if (val & I40E_PFLAN_QALLOC_VALID_MASK && j >= base_queue)
                num_queues = (j - base_queue) + 1;
        else
                num_queues = 0;
@@ -1092,7 +1092,7 @@ void i40e_clear_hw(struct i40e_hw *hw)
            I40E_PF_VT_PFALLOC_FIRSTVF_SHIFT;
        j = (val & I40E_PF_VT_PFALLOC_LASTVF_MASK) >>
            I40E_PF_VT_PFALLOC_LASTVF_SHIFT;
-       if (val & I40E_PF_VT_PFALLOC_VALID_MASK)
+       if (val & I40E_PF_VT_PFALLOC_VALID_MASK && j >= i)
                num_vfs = (j - i) + 1;
        else
                num_vfs = 0;