diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2021-08-27 18:15:13 +0300 |
---|---|---|
committer | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2021-09-01 13:09:17 +0300 |
commit | c0ca2cbdc2dfcca50d7bc71a93629ca8e02b1682 (patch) | |
tree | 820d9e27baac59f5bdf4cff7d3c10265c086c8e6 /src | |
parent | 29b372a56af2978b9f6bd428bdaaeff35bcca089 (diff) |
libcamera: base: log: Don't crash when logging after Logger destruction
libcamera isn't supposed to log messages after the logger is destroyed,
as the global logger instance is destroyed after the main() function
returns, and the camera manager is supposed to have been stopped and
destroyed before that.
This rule is difficult to enforce in the V4L2 compat implementation, as
there is no location where we can destroy the camera manager manually
before the logger is destroyed. This results in a use-after-free
condition when the camera manager gets stopped during destruction.
Fix it by not trying to print log messages when the global logger
instance has been destroyed.
This is a bit of a hack, but hopefully not too bad. There could be race
conditions when using a CameraManager instance that is destroyed as part
of the destruction of global variables (like the V4L2 compat layer does,
it wraps CameraManager in a singleton V4L2CompatManager class, and
destroys it when V4L2CompatManager is destroyed) as the CameraManager
thread will still be running when the logger gets destroyed, but this
doesn't cause any regression as we destroy the logger without any
safeguard measure today anyway.
There are other options that could be considered. Forcing destruction of
the logger after the camera manager in the V4L2 compat layer is one of
them, but turned out to be difficult. For instance care would need to be
taken *not* to log any message in the mmap() wrapper if the fd doesn't
match a wrapped camera, as mmap() is called very early in the
initialization process, before libcamera and the logger get initialized.
The resulting implementation would likely be fairly complex.
Another option could be to wrap the logger with a shared pointer, and
keep a reference to it in CameraManager. That's more intrusive, and it's
not clear if it would be worth it.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/libcamera/base/log.cpp | 18 |
1 files changed, 16 insertions, 2 deletions
diff --git a/src/libcamera/base/log.cpp b/src/libcamera/base/log.cpp index 073b7c34..a3e3f9ea 100644 --- a/src/libcamera/base/log.cpp +++ b/src/libcamera/base/log.cpp @@ -270,12 +270,16 @@ private: friend LogCategory; void registerCategory(LogCategory *category); + static bool destroyed_; + std::unordered_set<LogCategory *> categories_; std::list<std::pair<std::string, LogSeverity>> levels_; std::shared_ptr<LogOutput> output_; }; +bool Logger::destroyed_ = false; + /** * \enum LoggingTarget * \brief Log destination type @@ -372,6 +376,8 @@ void logSetLevel(const char *category, const char *level) Logger::~Logger() { + destroyed_ = true; + for (LogCategory *category : categories_) delete category; } @@ -387,6 +393,10 @@ Logger::~Logger() Logger *Logger::instance() { static Logger instance; + + if (destroyed_) + return nullptr; + return &instance; } @@ -808,13 +818,17 @@ LogMessage::~LogMessage() if (severity_ == LogInvalid) return; + Logger *logger = Logger::instance(); + if (!logger) + return; + msgStream_ << std::endl; if (severity_ >= category_.severity()) - Logger::instance()->write(*this); + logger->write(*this); if (severity_ == LogSeverity::LogFatal) { - Logger::instance()->backtrace(); + logger->backtrace(); std::abort(); } } |