/* * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "zm_catch2.h" #include "zm_logger.h" #include "zm_utils.h" #include #include #include #include #include #include #include static std::string slurp(const std::string &path) { std::ifstream in(path); std::stringstream ss; ss << in.rdbuf(); return ss.str(); } // zmc and zm_rtsp_server reload their configuration on SIGHUP and call // logReload() from their main loop. Other threads keep logging through that, // the dbQueue thread and the stream socket threads among them, each through // the instance Logger::fetch() gave it. The reload must leave that instance // valid. When the reload deleted and recreated the logger // (logTerm(); logInit()), AddressSanitizer reported a heap-use-after-free here. TEST_CASE("logReload while other threads log keeps the logger they hold") { std::string path = stringtf("/tmp/zm_test_logreload_%d.log", getpid()); unlink(path.c_str()); setenv("LOG_FLUSH", "1", 1); Logger::Options options(Logger::NOLOG, Logger::NOLOG, Logger::INFO, Logger::NOLOG); options.mLogFile = path; logInit("zm_test_logreload", options); Logger *before = Logger::fetch(); std::atomic stop(false); std::atomic lines(0); std::vector workers; for (int t = 0; t < 4; t++) { workers.emplace_back([&stop, &lines, t]() { while (!stop) { Info("worker %d line %d", t, lines++); } }); } for (int i = 0; i < 300; i++) { logReload("zm_test_logreload", options); } stop = true; for (std::thread &worker : workers) worker.join(); CHECK(Logger::fetch() == before); Info("after the reloads"); CHECK(slurp(path).find("after the reloads") != std::string::npos); CHECK(lines > 0); logTerm(); unlink(path.c_str()); }