Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpiolib: cdev: Disallow reconfiguration without direction (uAPI v1)

linehandle_set_config() behaves badly when direction is not set.
The configuration validation is borrowed from linehandle_create(), where,
to verify the intent of the user, the direction must be set to in order
to effect a change to the electrical configuration of a line. But, when
applied to reconfiguration, that validation does not allow for the unset
direction case, making it possible to clear flags set previously without
specifying the line direction.

Adding to the inconsistency, those changes are not immediately applied by
linehandle_set_config(), but will take effect when the line value is next
get or set.

For example, by requesting a configuration with no flags set, an output
line with GPIOHANDLE_REQUEST_ACTIVE_LOW and GPIOHANDLE_REQUEST_OPEN_DRAIN
requested could have those flags cleared, inverting the sense of the line
and changing the line drive to push-pull on the next line value set.

Ensure the intent of the user by disallowing configurations which do not
have direction set, returning an error to userspace to indicate that the
configuration is invalid.

And, for clarity, use lflags, a local copy of gcnf.flags, throughout when
dealing with the requested flags, rather than a mixture of both.

Fixes: e588bb1eae31 ("gpio: add new SET_CONFIG ioctl() to gpio chardev")
Signed-off-by: Kent Gibson <warthog618@gmail.com>
Link: https://lore.kernel.org/r/20240626052925.174272-2-warthog618@gmail.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

authored by

Kent Gibson and committed by
Bartosz Golaszewski
9919cce6 88811957

+10 -6
+10 -6
drivers/gpio/gpiolib-cdev.c
··· 89 89 GPIOHANDLE_REQUEST_OPEN_DRAIN | \ 90 90 GPIOHANDLE_REQUEST_OPEN_SOURCE) 91 91 92 + #define GPIOHANDLE_REQUEST_DIRECTION_FLAGS \ 93 + (GPIOHANDLE_REQUEST_INPUT | \ 94 + GPIOHANDLE_REQUEST_OUTPUT) 95 + 92 96 static int linehandle_validate_flags(u32 flags) 93 97 { 94 98 /* Return an error if an unknown flag is set */ ··· 173 169 if (ret) 174 170 return ret; 175 171 172 + /* Lines must be reconfigured explicitly as input or output. */ 173 + if (!(lflags & GPIOHANDLE_REQUEST_DIRECTION_FLAGS)) 174 + return -EINVAL; 175 + 176 176 for (i = 0; i < lh->num_descs; i++) { 177 177 desc = lh->descs[i]; 178 - linehandle_flags_to_desc_flags(gcnf.flags, &desc->flags); 178 + linehandle_flags_to_desc_flags(lflags, &desc->flags); 179 179 180 - /* 181 - * Lines have to be requested explicitly for input 182 - * or output, else the line will be treated "as is". 183 - */ 184 180 if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { 185 181 int val = !!gcnf.default_values[i]; 186 182 187 183 ret = gpiod_direction_output(desc, val); 188 184 if (ret) 189 185 return ret; 190 - } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { 186 + } else { 191 187 ret = gpiod_direction_input(desc); 192 188 if (ret) 193 189 return ret;