libcamera: device_enumerator_udev: Handle duplicate devices

It is possible that same device is processed multiple times, leading to
multiple `MediaDevice`s being instantiated, mostly likely leading to
a fatal error:

  Trying to register a camera with a duplicated ID xyzw...

There is a time window after the `udev_monitor` has been created in `init()`
and the first (and only) enumeration done in `enumerate()`. If e.g. a UVC
camera is connected in this time frame, then it is possible that it will be
processed both by the `udevNotify()` and the initial `enumerate()`, leading
to the fatal error. This can be reproduced as follows:

  1. $ gdb --args cam -m
  2. (gdb) break libcamera::DeviceEnumeratorUdev::enumerate
  3. (gdb) run
  4. when the breakpoint is hit, connect a usb camera
  5. (gdb) continue
  6. observe fatal error

To address this, keep track of the devnums of all devices reported by
udev, and reject devices with already known devnums. This ensures that
the same device won't be reported multiple times (assuming that udev
reports "add" / "remove" events in the correct order).

Closes: https://gitlab.freedesktop.org/camera/libcamera/-/issues/293
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
This commit is contained in:
Barnabás Pőcze
2025-12-02 14:17:48 +01:00
parent bb021aa549
commit 2b266a4ab2
2 changed files with 32 additions and 9 deletions
@@ -13,6 +13,7 @@
#include <set>
#include <string>
#include <sys/types.h>
#include <unordered_set>
#include <libcamera/base/class.h>
@@ -59,6 +60,7 @@ private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(DeviceEnumeratorUdev)
int addUdevDevice(struct udev_device *dev);
void removeUdevDevice(struct udev_device *dev);
int populateMediaDevice(MediaDevice *media, DependencyMap *deps);
std::string lookupDeviceNode(dev_t devnum);
@@ -70,6 +72,7 @@ private:
EventNotifier *notifier_;
std::set<dev_t> orphans_;
std::unordered_set<dev_t> devices_;
std::list<MediaDeviceDeps> pending_;
std::map<dev_t, MediaDeviceDeps *> devMap_;
};