The v4l2_ctrl_type_op_init mixed up the meaning of the 'elems' argument: in a for-loop it was the total number of elements, but in memset it was the number of elements to be cleared. This is the same as long as from_idx == 0, but when it is non-0, then it will cause a memory overrun in the memset case. Rename the 'elems' argument to 'tot_elems' and use that in the for loops. Add a new 'elems' variable that is the number of elements to clear. Since that's what memset expects, no changes are needed there. Also add a check to see if there is anything to initialize at all, and just return if there is nothing to be done. Fixes: b19b54e9ccd8 (media: v4l2-ctrls: optimize type_ops for arrays) Signed-off-by: Hans Verkuil <hverkuil-cisco@xxxxxxxxx> --- diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 5d1a89884b28..01f00093f259 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -178,13 +178,17 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx, } void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, - u32 elems, union v4l2_ctrl_ptr ptr) + u32 tot_elems, union v4l2_ctrl_ptr ptr) { unsigned int i; + u32 elems = tot_elems - from_idx; + + if (from_idx >= tot_elems) + return; switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: - for (i = from_idx; i < elems; i++) { + for (i = from_idx; i < tot_elems; i++) { unsigned int offset = i * ctrl->elem_size; memset(ptr.p_char + offset, ' ', ctrl->minimum); @@ -193,7 +197,7 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, break; case V4L2_CTRL_TYPE_INTEGER64: if (ctrl->default_value) { - for (i = from_idx; i < elems; i++) + for (i = from_idx; i < tot_elems; i++) ptr.p_s64[i] = ctrl->default_value; } else { memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64)); @@ -205,7 +209,7 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BOOLEAN: if (ctrl->default_value) { - for (i = from_idx; i < elems; i++) + for (i = from_idx; i < tot_elems; i++) ptr.p_s32[i] = ctrl->default_value; } else { memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32)); @@ -220,7 +224,7 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, break; case V4L2_CTRL_TYPE_U16: if (ctrl->default_value) { - for (i = from_idx; i < elems; i++) + for (i = from_idx; i < tot_elems; i++) ptr.p_u16[i] = ctrl->default_value; } else { memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16)); @@ -228,14 +232,14 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx, break; case V4L2_CTRL_TYPE_U32: if (ctrl->default_value) { - for (i = from_idx; i < elems; i++) + for (i = from_idx; i < tot_elems; i++) ptr.p_u32[i] = ctrl->default_value; } else { memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32)); } break; default: - for (i = from_idx; i < elems; i++) + for (i = from_idx; i < tot_elems; i++) std_init_compound(ctrl, i, ptr); break; } diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index b2acb64d55aa..b76a0714d425 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -131,7 +131,7 @@ struct v4l2_ctrl_type_ops { bool (*equal)(const struct v4l2_ctrl *ctrl, u32 elems, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2); - void (*init)(const struct v4l2_ctrl *ctrl, u32 from_idx, u32 elems, + void (*init)(const struct v4l2_ctrl *ctrl, u32 from_idx, u32 tot_elems, union v4l2_ctrl_ptr ptr); void (*log)(const struct v4l2_ctrl *ctrl); int (*validate)(const struct v4l2_ctrl *ctrl, u32 elems,