Commit e64d1cb2 authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Bartosz Golaszewski
Browse files

gpiolib: acpi: Fix potential out-of-boundary left shift



GPIO Address Space handler gets a pointer to the in or out value.
This value is supposed to be at least 64-bit, but it's not limited
to be exactly 64-bit. When ACPI tables are being parsed, for
the bigger Connection():s ACPICA creates a Buffer instead of regular
Integer object. The Buffer exists as long as Namespace holds
the certain Connection(). Hence we can access the necessary bits
without worrying. On the other hand, the left shift, used in
the code, is limited by 31 (on 32-bit platforms) and otherwise
considered to be Undefined Behaviour. Also the code uses only
the first 64-bit word for the value, and anything bigger than 63
will be also subject to UB. Fix all this by modifying the code
to correctly set or clear the respective bit in the bitmap constructed
of 64-bit words.

Fixes: 59084c564c41 ("gpiolib: acpi: use BIT_ULL() for u64 mask in address space handler")
Fixes: 2c4d00cb ("gpiolib: acpi: Use BIT() macro to increase readability")
Cc: stable@vger.kernel.org
Reviewed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://patch.msgid.link/20260128095918.4157491-1-andriy.shevchenko@linux.intel.com


Signed-off-by: default avatarBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
parent b2cf569e
Loading
Loading
Loading
Loading
+17 −4
Original line number Diff line number Diff line
@@ -1104,6 +1104,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
		unsigned int pin = agpio->pin_table[i];
		struct acpi_gpio_connection *conn;
		struct gpio_desc *desc;
		u16 word, shift;
		bool found;

		mutex_lock(&achip->conn_lock);
@@ -1158,10 +1159,22 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,

		mutex_unlock(&achip->conn_lock);

		if (function == ACPI_WRITE)
			gpiod_set_raw_value_cansleep(desc, !!(*value & BIT_ULL(i)));
		/*
		 * For the cases when OperationRegion() consists of more than
		 * 64 bits calculate the word and bit shift to use that one to
		 * access the value.
		 */
		word = i / 64;
		shift = i % 64;

		if (function == ACPI_WRITE) {
			gpiod_set_raw_value_cansleep(desc, value[word] & BIT_ULL(shift));
		} else {
			if (gpiod_get_raw_value_cansleep(desc))
				value[word] |= BIT_ULL(shift);
			else
			*value |= (u64)gpiod_get_raw_value_cansleep(desc) << i;
				value[word] &= ~BIT_ULL(shift);
		}
	}

out: