counter: 104-quad-8: Fix race getting function mode and direction

The quad8_action_read() function checks the Count function mode and
Count direction without first acquiring a lock. This is a race condition
because the function mode could change by the time the direction is
checked.

Because the quad8_function_read() already acquires a lock internally,
the quad8_function_read() is refactored to spin out the no-lock code to
a new quad8_function_get() function.

To resolve the race condition in quad8_action_read(), a lock is acquired
before calling quad8_function_get() and quad8_direction_read() in order
to get both function mode and direction atomically.

Fixes: f1d8a071d45b ("counter: 104-quad-8: Add Generic Counter interface support")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20221020141121.15434-1-william.gray@linaro.org/
Signed-off-by: William Breathitt Gray <william.gray@linaro.org>

Changed files
+42 -22
drivers
counter
+42 -22
drivers/counter/104-quad-8.c
··· 232 232 COUNTER_FUNCTION_QUADRATURE_X4, 233 233 }; 234 234 235 + static int quad8_function_get(const struct quad8 *const priv, const size_t id, 236 + enum counter_function *const function) 237 + { 238 + if (!priv->quadrature_mode[id]) { 239 + *function = COUNTER_FUNCTION_PULSE_DIRECTION; 240 + return 0; 241 + } 242 + 243 + switch (priv->quadrature_scale[id]) { 244 + case 0: 245 + *function = COUNTER_FUNCTION_QUADRATURE_X1_A; 246 + return 0; 247 + case 1: 248 + *function = COUNTER_FUNCTION_QUADRATURE_X2_A; 249 + return 0; 250 + case 2: 251 + *function = COUNTER_FUNCTION_QUADRATURE_X4; 252 + return 0; 253 + default: 254 + /* should never reach this path */ 255 + return -EINVAL; 256 + } 257 + } 258 + 235 259 static int quad8_function_read(struct counter_device *counter, 236 260 struct counter_count *count, 237 261 enum counter_function *function) 238 262 { 239 263 struct quad8 *const priv = counter_priv(counter); 240 - const int id = count->id; 241 264 unsigned long irqflags; 265 + int retval; 242 266 243 267 spin_lock_irqsave(&priv->lock, irqflags); 244 268 245 - if (priv->quadrature_mode[id]) 246 - switch (priv->quadrature_scale[id]) { 247 - case 0: 248 - *function = COUNTER_FUNCTION_QUADRATURE_X1_A; 249 - break; 250 - case 1: 251 - *function = COUNTER_FUNCTION_QUADRATURE_X2_A; 252 - break; 253 - case 2: 254 - *function = COUNTER_FUNCTION_QUADRATURE_X4; 255 - break; 256 - } 257 - else 258 - *function = COUNTER_FUNCTION_PULSE_DIRECTION; 269 + retval = quad8_function_get(priv, count->id, function); 259 270 260 271 spin_unlock_irqrestore(&priv->lock, irqflags); 261 272 262 - return 0; 273 + return retval; 263 274 } 264 275 265 276 static int quad8_function_write(struct counter_device *counter, ··· 370 359 enum counter_synapse_action *action) 371 360 { 372 361 struct quad8 *const priv = counter_priv(counter); 362 + unsigned long irqflags; 373 363 int err; 374 364 enum counter_function function; 375 365 const size_t signal_a_id = count->synapses[0].signal->id; ··· 386 374 return 0; 387 375 } 388 376 389 - err = quad8_function_read(counter, count, &function); 390 - if (err) 377 + spin_lock_irqsave(&priv->lock, irqflags); 378 + 379 + /* Get Count function and direction atomically */ 380 + err = quad8_function_get(priv, count->id, &function); 381 + if (err) { 382 + spin_unlock_irqrestore(&priv->lock, irqflags); 391 383 return err; 384 + } 385 + err = quad8_direction_read(counter, count, &direction); 386 + if (err) { 387 + spin_unlock_irqrestore(&priv->lock, irqflags); 388 + return err; 389 + } 390 + 391 + spin_unlock_irqrestore(&priv->lock, irqflags); 392 392 393 393 /* Default action mode */ 394 394 *action = COUNTER_SYNAPSE_ACTION_NONE; ··· 413 389 return 0; 414 390 case COUNTER_FUNCTION_QUADRATURE_X1_A: 415 391 if (synapse->signal->id == signal_a_id) { 416 - err = quad8_direction_read(counter, count, &direction); 417 - if (err) 418 - return err; 419 - 420 392 if (direction == COUNTER_COUNT_DIRECTION_FORWARD) 421 393 *action = COUNTER_SYNAPSE_ACTION_RISING_EDGE; 422 394 else