Skip to content

Commit 96ed45b

Browse files
committed
libcamera: v4l2_device: Workaround faulty control menus
Some UVC cameras have been identified that can provide V4L2 menu controls without any menu items. This leads to a segfault where we try to construct a ControlInfo(Span<>,default) with an empty span. Convert the v4l2ControlInfo and v4l2MenuControlInfo helper functions to return std::optional<ControlInfo> to be able to account in the caller if the control is valid, and only add acceptable controls to the supported control list. Menu controls without a list of menu items are no longer added as a valid control and a warning is logged. This also fixes a potential crash that would have occured in the unlikely event that a ctrl.minimum was set to less than 0. Bug: https://bugs.libcamera.org/show_bug.cgi?id=167 Reported-by: Marian Buschsieweke <[email protected]> Reviewed-by: Jacopo Mondi <[email protected]> Reviewed-by: Laurent Pinchart <[email protected]> Signed-off-by: Kieran Bingham <[email protected]>
1 parent 1cd7646 commit 96ed45b

File tree

2 files changed

+25
-7
lines changed

2 files changed

+25
-7
lines changed

include/libcamera/internal/v4l2_device.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ class V4L2Device : protected Loggable
7070
private:
7171
static ControlType v4l2CtrlType(uint32_t ctrlType);
7272
static std::unique_ptr<ControlId> v4l2ControlId(const v4l2_query_ext_ctrl &ctrl);
73-
ControlInfo v4l2ControlInfo(const v4l2_query_ext_ctrl &ctrl);
74-
ControlInfo v4l2MenuControlInfo(const v4l2_query_ext_ctrl &ctrl);
73+
std::optional<ControlInfo> v4l2ControlInfo(const v4l2_query_ext_ctrl &ctrl);
74+
std::optional<ControlInfo> v4l2MenuControlInfo(const v4l2_query_ext_ctrl &ctrl);
7575

7676
void listControls();
7777
void updateControls(ControlList *ctrls,

src/libcamera/v4l2_device.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ std::unique_ptr<ControlId> V4L2Device::v4l2ControlId(const v4l2_query_ext_ctrl &
529529
* \param[in] ctrl The v4l2_query_ext_ctrl that represents a V4L2 control
530530
* \return A ControlInfo that represents \a ctrl
531531
*/
532-
ControlInfo V4L2Device::v4l2ControlInfo(const v4l2_query_ext_ctrl &ctrl)
532+
std::optional<ControlInfo> V4L2Device::v4l2ControlInfo(const v4l2_query_ext_ctrl &ctrl)
533533
{
534534
switch (ctrl.type) {
535535
case V4L2_CTRL_TYPE_U8:
@@ -566,14 +566,14 @@ ControlInfo V4L2Device::v4l2ControlInfo(const v4l2_query_ext_ctrl &ctrl)
566566
*
567567
* \return A ControlInfo that represents \a ctrl
568568
*/
569-
ControlInfo V4L2Device::v4l2MenuControlInfo(const struct v4l2_query_ext_ctrl &ctrl)
569+
std::optional<ControlInfo> V4L2Device::v4l2MenuControlInfo(const struct v4l2_query_ext_ctrl &ctrl)
570570
{
571571
std::vector<ControlValue> indices;
572572
struct v4l2_querymenu menu = {};
573573
menu.id = ctrl.id;
574574

575575
if (ctrl.minimum < 0)
576-
return ControlInfo();
576+
return std::nullopt;
577577

578578
for (int32_t index = ctrl.minimum; index <= ctrl.maximum; ++index) {
579579
menu.index = index;
@@ -583,6 +583,14 @@ ControlInfo V4L2Device::v4l2MenuControlInfo(const struct v4l2_query_ext_ctrl &ct
583583
indices.push_back(index);
584584
}
585585

586+
/*
587+
* Some faulty UVC devices are known to return an empty menu control.
588+
* Controls without a menu option can not be set, or read, so they are
589+
* not exposed.
590+
*/
591+
if (indices.size() == 0)
592+
return std::nullopt;
593+
586594
return ControlInfo(indices,
587595
ControlValue(static_cast<int32_t>(ctrl.default_value)));
588596
}
@@ -631,7 +639,17 @@ void V4L2Device::listControls()
631639
controlIdMap_[ctrl.id] = controlIds_.back().get();
632640
controlInfo_.emplace(ctrl.id, ctrl);
633641

634-
ctrls.emplace(controlIds_.back().get(), v4l2ControlInfo(ctrl));
642+
std::optional<ControlInfo> info = v4l2ControlInfo(ctrl);
643+
644+
if (!info) {
645+
LOG(V4L2, Error)
646+
<< "Control " << ctrl.name
647+
<< " cannot be registered";
648+
649+
continue;
650+
}
651+
652+
ctrls.emplace(controlIds_.back().get(), *info);
635653
}
636654

637655
controls_ = ControlInfoMap(std::move(ctrls), controlIdMap_);
@@ -670,7 +688,7 @@ void V4L2Device::updateControlInfo()
670688
continue;
671689
}
672690

673-
info = v4l2ControlInfo(ctrl);
691+
info = *v4l2ControlInfo(ctrl);
674692
}
675693
}
676694

0 commit comments

Comments
 (0)