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

[media] v4l2-ctrls: fix rounding calculation

Commit 958c7c7e65 ("[media] v4l2-ctrls: fix corner case in round-to-range code") broke
controls that use a negative range.

The cause was a s32/u32 mixup: ctrl->step is unsigned while all others are signed. So
the result type of the expression '(ctrl)->maximum - ((ctrl)->step / 2)' became unsigned,
making 'val >= (ctrl)->maximum - ((ctrl)->step / 2)' true, since '((u32)-128) > 128'
(if val = -128, maximum = 128 and step = 1).

So carefully cast (step / 2) to s32.

There was one cast of step to s32 where it should have been u32 because both offset and
step are unsigned, so casting to signed makes no sense there. You do need a cast to u32
there, because otherwise architectures that have no 64-bit division start complaining
(step is a u64).

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Reported-by: Frank Schäfer <fschaefer.oss@googlemail.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>

authored by

Hans Verkuil and committed by
Mauro Carvalho Chehab
9c9cb1fa 5b8c8d41

+5 -5
+5 -5
drivers/media/v4l2-core/v4l2-ctrls.c
··· 1347 1347 ({ \ 1348 1348 offset_type offset; \ 1349 1349 if ((ctrl)->maximum >= 0 && \ 1350 - val >= (ctrl)->maximum - ((ctrl)->step / 2)) \ 1350 + val >= (ctrl)->maximum - (s32)((ctrl)->step / 2)) \ 1351 1351 val = (ctrl)->maximum; \ 1352 1352 else \ 1353 - val += (ctrl)->step / 2; \ 1353 + val += (s32)((ctrl)->step / 2); \ 1354 1354 val = clamp_t(typeof(val), val, \ 1355 1355 (ctrl)->minimum, (ctrl)->maximum); \ 1356 1356 offset = (val) - (ctrl)->minimum; \ 1357 - offset = (ctrl)->step * (offset / (s32)(ctrl)->step); \ 1357 + offset = (ctrl)->step * (offset / (u32)(ctrl)->step); \ 1358 1358 val = (ctrl)->minimum + offset; \ 1359 1359 0; \ 1360 1360 }) ··· 1376 1376 * the u64 divide that needs special care. 1377 1377 */ 1378 1378 val = ptr.p_s64[idx]; 1379 - if (ctrl->maximum >= 0 && val >= ctrl->maximum - ctrl->step / 2) 1379 + if (ctrl->maximum >= 0 && val >= ctrl->maximum - (s64)(ctrl->step / 2)) 1380 1380 val = ctrl->maximum; 1381 1381 else 1382 - val += ctrl->step / 2; 1382 + val += (s64)(ctrl->step / 2); 1383 1383 val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum); 1384 1384 offset = val - ctrl->minimum; 1385 1385 do_div(offset, ctrl->step);