Repository navigation
Conversation
MonitorLinkExpression kept a string_view of the string it was built from, Monitor::linked_monitors_string, and the tokens parse() builds are views into it. Monitor::Reload() calls Load(), which reassigns that string without re-parsing the expression, so once the new value no longer fits the old buffer every token points into freed memory. They are read by the Debug(1, ...) calls in MonitorLinkExpression::visit(), which runs on every analysed frame of a monitor with LinkedMonitors set when its debug level is 1 or more. Copy the string into the expression and point the views at the copy. fixes ZoneMinder#5271 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 12, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Impact: fix-medium (use-after-free in the linked-monitor expression after zmu -L (admin action, read at debug level 1+))
TL;DR
MonitorLinkExpression::visitafter azmu -Lreload changes LinkedMonitors.MonitorLinkExpressioncopies the expression into aconst std::stringmember and points itsstring_view(and so every token) at that copy; copying the object is deleted so the views can't follow a moved string; 3 files, +50/-1Tokenis untouched. The events check's linked-monitor scenario passed.Details: reproduction, change, checks
Before
Upstream master
233a15828, clean ASan build ("head": "233a1582829edc49554a0f1a9bc36d61dd951aaf", "branch": "master", "dirty": false):New unit test on master's code (plain build): the freed bytes were parsed as a monitor id:
Change
src/zm_monitorlink_expression.h: new memberconst std::string expression_string_, declaredbefore
expression_and initialised from the constructor argument;expression_views it.Copy constructor and assignment deleted (this also suppresses the implicit move, which would
leave the views pointing into the moved-from string); the class is only ever created with
new.tests/zm_monitorlink_expression.cpp(new, added totests/CMakeLists.txt): builds anexpression from a heap-sized string of delimiters (no monitor ids, so no database), reassigns
and destroys the string, then
parse()andresult(). Fails before (abort or ASan report),passes after.
Behaviour changes
zmu -LreloadReload()(would also apply the new LinkedMonitors at once, but deletes the expression the analysis thread may be evaluating: a new use-after-free unless Reload and Analyse are synchronised);std::stringinToken(drops theconstexprmembers, the-Winvalid-constexprproblem #5219 met)zmu -L, the old linked monitors stay in use until the next reconnect (as before)Side fixes in this PR: none.
After
Branch
fix/b81-monitorlink-own-expressionat2f7ace2c2(base233a15828), tested in a merge withfour other fixes from the same lab session (none touches linked monitors):
check-changed.pyon the plain -Werror combo505d0a5ff, which had this fix asc9a7f7be3;2f7ace2c2only moves thetests/CMakeLists.txtline)zmonvif-probe.pldiscovery), resilience PASS=5, events PASS=10 (incl. linked), dblog --quick PASS=1 XFAIL=2 (known on master, #5197/#5208), zmscmd --quick PASS=4 XFAIL=1 XPASS=3 (XFAIL speed = B75/#5229; XPASS = B66/B73/B74 fixed on master)-Werrorbuild + unit tests/usr/share/zm-holodeck/werror), All tests passed (14778 assertions in 260 test cases)75a68dd/ ha-zoneminder69dc880/ zmNinjaNg3f90042test_monitor_entities_match_serverpassed on re-run) / passed=39Not run: the deep set. Backport to release-1.38: same code (
zm_monitorlink_expression.h:94,102on1a830d7d1), read; the patch should apply (not built).Tested as
a998c6235;2f7ace2c2is the same tree withfixes #5271added to the message.fixes #5271
🤖 Generated with Claude Code