Found while fixing #4008 (plan-capture spoofing). Not fixed here: different collector, needs its own PoC verification, regex redesign and regression tests, and #4008's deadline did not leave room for it.
What is wrong
PgDeadlockLogParser.s_deadlockBlock (the C# parser, used by the RDS log-API route) and PgDeadlocksCollector's self-hosted regexp_matches both anchor to a genuine line start and require a real timestamp before the pid bracket — the fix #4008 gives plan capture. But the pattern after the bracket is a WILDCARD, not a tight match:
[^\n]*ERROR: deadlock detected\s*\n[^\n]*DETAIL: (...)
[^\n]* allows ANY text between the bracket and ERROR: deadlock detected, including a REAL field label like STATEMENT: . So a backend's own STATEMENT companion, if its echoed SQL happens to contain the literal text ERROR: deadlock detected (e.g. in a comment) followed by a tab-continued line reading DETAIL: Process N waits for ... ; blocked by process M., matches the pattern using the REAL pid bracket already on that line — no backtracking trick even required, [^\n]* reaches it directly.
Sketch (not yet run live): a syntax error whose offending SQL is
SELECT 1 -- ERROR: deadlock detected
DETAIL: Process 1 waits for ShareLock on transaction 5; blocked by process 2.
Process 2 waits for ShareLock on transaction 6; blocked by process 1.
logged under any backend pid, would attribute a forged deadlock (fake participants, fake resource) to that real backend.
Separately, the MANAGED-prefix branch of both the deadlock and (pre-#4008) plan parsers used a LAZY [^\n]*? gap before the pid bracket, which backtracks past a real bracket to reach a forged one later on the same line when the tail pattern fails at the real one. #4008 closed that for plan capture by excluding [ from the gap's character class ([^\[\n]*, deterministic — see PgPlanLogParser.s_planBlock's comment and PgPlanLogParserTests.AForgedHeaderBehindARealColonPrefixedBracket_StillYieldsNoPlan). PgDeadlockLogParser.s_deadlockBlock still has the lazy form and needs the same treatment, independent of the wildcard-gap issue above.
Suggested fix
Found while fixing #4008 (plan-capture spoofing). Not fixed here: different collector, needs its own PoC verification, regex redesign and regression tests, and #4008's deadline did not leave room for it.
What is wrong
PgDeadlockLogParser.s_deadlockBlock(the C# parser, used by the RDS log-API route) andPgDeadlocksCollector's self-hostedregexp_matchesboth anchor to a genuine line start and require a real timestamp before the pid bracket — the fix #4008 gives plan capture. But the pattern after the bracket is a WILDCARD, not a tight match:[^\n]*allows ANY text between the bracket andERROR: deadlock detected, including a REAL field label likeSTATEMENT:. So a backend's own STATEMENT companion, if its echoed SQL happens to contain the literal textERROR: deadlock detected(e.g. in a comment) followed by a tab-continued line readingDETAIL: Process N waits for ... ; blocked by process M., matches the pattern using the REAL pid bracket already on that line — no backtracking trick even required,[^\n]*reaches it directly.Sketch (not yet run live): a syntax error whose offending SQL is
logged under any backend pid, would attribute a forged deadlock (fake participants, fake resource) to that real backend.
Separately, the MANAGED-prefix branch of both the deadlock and (pre-#4008) plan parsers used a LAZY
[^\n]*?gap before the pid bracket, which backtracks past a real bracket to reach a forged one later on the same line when the tail pattern fails at the real one. #4008 closed that for plan capture by excluding[from the gap's character class ([^\[\n]*, deterministic — seePgPlanLogParser.s_planBlock's comment andPgPlanLogParserTests.AForgedHeaderBehindARealColonPrefixedBracket_StillYieldsNoPlan).PgDeadlockLogParser.s_deadlockBlockstill has the lazy form and needs the same treatment, independent of the wildcard-gap issue above.Suggested fix
[^\n]*ERROR: deadlock detected(both the C# pattern and the self-hosted SQL) to not cross a field-label boundary — mirror Plan capture reads a plan block anywhere in a log line, so a statement's author can attach a forged plan to any query id #4008's reasoning rather than re-deriving it.PgPlanLogParser's[^\[\n]*fix tos_deadlockBlock's managed-prefix branch.logging_collector = onbefore/after, the same way Plan capture reads a plan block anywhere in a log line, so a statement's author can attach a forged plan to any query id #4008'sDarling.Tests/PgPlanCaptureLiveTests.csdoes for plan capture.