Re: [PATCH v7 4/6] PCI: Refactor disable_acs_redir and config_acs param handling

Wei Wang <[email protected]> Wed, 13 May 2026 18:09:46 +0800
Newsgroups dev.linux.lists.sashiko,org.kernel.vger.linux-pci
Message-ID <SI2PR01MB4393B797DFA5AD23FEBC6D6FDC062@SI2PR01MB4393.apcprd01.prod.exchangelabs.com>
On 5/7/26 5:07 AM, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] Bounds checking in pci_param_config_acs() breaks backwards compatibility for padded parameters and contains an integer underflow that leads to Undefined Behavior.
> - [Medium] Silenced logging for disable_acs_redir reduces configuration observability.
> --
> 
> commit 2b4c7af1c4a60302203ef348c929aa8cf673e3ac
> Author: Wei Wang <[email protected]>
> 
> PCI: Refactor disable_acs_redir and config_acs param handling
> 
> This commit refactors the ACS parameter handling by splitting the old
> __pci_config_acs function into dedicated functions for disable_acs_redir
> and config_acs parameters.
> 
>> --- a/drivers/pci/pci.c
>> +++ b/drivers/pci/pci.c
> 
> [ ... ]
> 
>> +static void pci_param_disable_acs_redir(struct pci_dev *dev,
>> +					struct pci_acs *caps)
>> +{
>> +	u16 acs_redir_mask = PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC;
>> +
>> +	if (!disable_acs_redir_param ||
>> +	    !pci_dev_match_disable_acs_redir(dev, disable_acs_redir_param) ||
>> +	    !pci_dev_specific_disable_acs_redir(dev))
>> +		return;
>> +
>> +	caps->ctrl = caps->fw_ctrl & ~acs_redir_mask;
>> +}
> 
> The previous implementation printed a pci_info message when ACS control was
> modified. Is it intentional that pci_param_disable_acs_redir no longer logs
> confirmation that the redirect was disabled?
> 
> This might make debugging device isolation issues more difficult without the
> dmesg confirmation.

Yes. I think it's better to have the pci_info message printed in
pci_enable_acs():

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b684ae4e92b5..dcc591d679d5 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1008,9 +1008,6 @@ static void pci_param_config_acs(struct pci_dev *dev, struct pci_acs *caps)
                 enabled_bits, disabled_bits);
  
         caps->ctrl = (caps->fw_ctrl | enabled_bits) & ~disabled_bits;
-
-       pci_info(dev, "Configured ACS to %#06x (FW default: %#06x)\n",
-                caps->ctrl, caps->fw_ctrl);
  }
  
  /**
@@ -1080,6 +1077,9 @@ void pci_enable_acs(struct pci_dev *dev)
         pci_param_disable_acs_redir(dev, &caps);
         pci_param_config_acs(dev, &caps);
  
+       pci_info(dev, "Configured ACS to %#06x (FW default: %#06x)\n",
+                caps->ctrl, caps->fw_ctrl);
+
         pci_write_config_word(dev, pos + PCI_ACS_CTRL, caps.ctrl);


> 
> [ ... ]
> 
>> +static void pci_param_config_acs(struct pci_dev *dev, struct pci_acs *caps)
>> +{
>> +	u16 shift = 0, max_shift = fls(dev->acs_capabilities) - 1;
> 
> If dev->acs_capabilities is 0, fls(0) evaluates to 0. Will 0 - 1 underflow
> the u16 max_shift variable to 65535, effectively bypassing the bounds check
> below?

pci_enable_acs() has already gated the execution via checking dev->acs_cap,
so if the acs_cap is 0, it should have returned earlier already.

It seems not possible that a device has an acs_cap but dev->acs_capabilities is
0, which means the device added acs cap but the cap is empty (no acs features).


> 
>> +	u16 enabled_bits = 0, disabled_bits = 0;
>> +	const char *p, *seg;
> 
> [ ... ]
> 
>> +	p = strchr(seg, '@');
>> +	/* Parse bitstring backwards from '@' */
>> +	while (p > seg) {
>> +		if (shift > max_shift) {
>> +			pci_err(dev, "ACS flag bit %d exceed range %d\n",
>> +				shift, max_shift);
>> +			return;
>> +		}
> 
> The previous implementation gracefully allowed parameter padding that exceeded
> the capability mask, only erroring if unsupported bits were actively enabled.
> 
> Does aborting here when shift exceeds max_shift break backwards compatibility
> for existing working kernel boot parameters (like config_acs=00000000@...)
> on devices with fewer capabilities?

Yes, use "break" here is better than "return". We should allow the valid
configs (< max_shift) applying to device. Zeroing those exceed the max_shift
isn't necessary, as the device does not support them.

> 
>> +
>> +		switch (*--p) {
>> +		case '1':
>> +			enabled_bits |= BIT(shift);
>> +			break;
> 
> If the bounds check is bypassed due to the max_shift underflow mentioned
> above, could shift increment without bounds for long user strings?
> 
> If shift exceeds the width of an unsigned long, BIT(shift) could trigger
> undefined behavior. Additionally, since enabled_bits is a u16, shift amounts
> of 16 or greater will silently truncate.

As mentioned above, max_shift wouldn't underflow, so it won't be an issue
here then.