Summary
contrib/devtools/circular-dependencies.py can miss a dependency cycle when a
valid trailing comment on an angle-bracket include contains a greater-than
character. Adding only a comment can change the tool's result from exit 1 with
a reported cycle to exit 0 with no output.
Version and scope
Repository: https://github.com/dashpay/dash
Branch snapshot: develop
Commit: c14104b172a271d69823173a7323b90ca9b32cde
Source:
|
RE = re.compile("^#include <(.*)>") |
Reproduced using Python 3.12.14 on Windows, exercising the serial execution path.
This is a development-tool correctness report, not a wallet, consensus, or
security-critical vulnerability report. The report and proposed fix were
prepared with AI assistance and checked by direct local executions.
Minimal reproduction
In an otherwise empty temporary directory, create these two files:
a.h:
#include <b.h> // dependency for Container<T>
b.h:
Invoke the pinned script from that directory, passing both relative names:
python /path/to/circular-dependencies.py a.h b.h
Expected: exit 1, with this output:
Circular dependency: a -> b -> a
Actual: exit 0, with no stdout or stderr.
Remove the trailing comment (or replace it with a comment without '>') and the
same two-file cycle is reported correctly. A /* Container<T> */ comment causes
the same miss. Neither example relies on quoted includes or unusual whitespace.
Cause
The expression ^#include <(.*)> is greedy. For the a.h example, the captured
text is b.h> // dependency for Container<T instead of b.h. The subsequent
module lookup cannot match that value, so the a-to-b edge is omitted.
Upstream context
Upstream context: source inspection of Bitcoin Core master at 66776840beb558f7e84451c2c55457f0e06242f0 found the same greedy include expression and module-lookup behavior in contrib/devtools/circular-dependencies.py. This is therefore not presented as a Dash-specific regression. Three bounded public issue/PR searches did not identify this exact trailing-comment trigger, but one search examined only the first 30 of 85 results and discussion histories were not exhaustively reviewed; prior-report status and reward eligibility remain unconfirmed.
Source: https://github.com/bitcoin/bitcoin/blob/66776840beb558f7e84451c2c55457f0e06242f0/contrib/devtools/circular-dependencies.py#L30
Suggested fix
Stop the header-name match at the first closing angle bracket:
RE = re.compile(r"^#include <([^>]+)>")
The attached patch changes only this expression. It preserves the existing
angle-bracket and beginning-of-line requirements and does not attempt to make
the tool a full C++ preprocessor. GCC's include-syntax documentation permits
comments after a header name:
https://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
Verification
The attached verify-fix.py requires the exact source SHA-256 before executing
it. It creates eight synthetic two-header cases and runs both the unchanged
script and the one-line patched script. It checks both exit status and exact
stdout/stderr; it does not test only the regex in isolation.
Observed:
- Unchanged source: four cases miss a cycle; four control cases behave correctly.
- Patched source: all eight cases behave as expected.
- Controls include ordinary cycles, ordinary comments, an acyclic graph, and
an acyclic graph with the same comment syntax.
- No network calls, node binaries, wallet files, or real repository data were
used during the reproduction.
Limits
The full repository lint suite, C++ build, functional tests, and Linux fork
execution path have not been run. No claim is made that a currently checked-in
Dash source file already triggers the problem. The direct caller in
test/lint/lint-circular-dependencies.py consumes this tool's output, so missed
edges can affect its cycle detection; a full CI run was not simulated.
A bounded search did not find this exact trigger already reported, but this is
not proof that no duplicate exists. No accepted fix, confirmed bounty eligibility, or payment is claimed.
Supporting files are attached as dash-core-circular-dependency-report-v2.zip.
This issue reports the technical defect only; it does not submit a bounty claim or request payment.
dash-core-circular-dependency-report-v2.zip
Summary
contrib/devtools/circular-dependencies.py can miss a dependency cycle when a
valid trailing comment on an angle-bracket include contains a greater-than
character. Adding only a comment can change the tool's result from exit 1 with
a reported cycle to exit 0 with no output.
Version and scope
Repository: https://github.com/dashpay/dash
Branch snapshot: develop
Commit:
c14104b172a271d69823173a7323b90ca9b32cdeSource:
dash/contrib/devtools/circular-dependencies.py
Line 66 in c14104b
Reproduced using Python 3.12.14 on Windows, exercising the serial execution path.
This is a development-tool correctness report, not a wallet, consensus, or
security-critical vulnerability report. The report and proposed fix were
prepared with AI assistance and checked by direct local executions.
Minimal reproduction
In an otherwise empty temporary directory, create these two files:
a.h:
b.h:
Invoke the pinned script from that directory, passing both relative names:
python /path/to/circular-dependencies.py a.h b.hExpected: exit 1, with this output:
Actual: exit 0, with no stdout or stderr.
Remove the trailing comment (or replace it with a comment without '>') and the
same two-file cycle is reported correctly. A
/* Container<T> */comment causesthe same miss. Neither example relies on quoted includes or unusual whitespace.
Cause
The expression
^#include <(.*)>is greedy. For the a.h example, the capturedtext is
b.h> // dependency for Container<Tinstead of b.h. The subsequentmodule lookup cannot match that value, so the a-to-b edge is omitted.
Upstream context
Upstream context: source inspection of Bitcoin Core master at
66776840beb558f7e84451c2c55457f0e06242f0found the same greedy include expression and module-lookup behavior in contrib/devtools/circular-dependencies.py. This is therefore not presented as a Dash-specific regression. Three bounded public issue/PR searches did not identify this exact trailing-comment trigger, but one search examined only the first 30 of 85 results and discussion histories were not exhaustively reviewed; prior-report status and reward eligibility remain unconfirmed.Source: https://github.com/bitcoin/bitcoin/blob/66776840beb558f7e84451c2c55457f0e06242f0/contrib/devtools/circular-dependencies.py#L30
Suggested fix
Stop the header-name match at the first closing angle bracket:
The attached patch changes only this expression. It preserves the existing
angle-bracket and beginning-of-line requirements and does not attempt to make
the tool a full C++ preprocessor. GCC's include-syntax documentation permits
comments after a header name:
https://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
Verification
The attached verify-fix.py requires the exact source SHA-256 before executing
it. It creates eight synthetic two-header cases and runs both the unchanged
script and the one-line patched script. It checks both exit status and exact
stdout/stderr; it does not test only the regex in isolation.
Observed:
an acyclic graph with the same comment syntax.
used during the reproduction.
Limits
The full repository lint suite, C++ build, functional tests, and Linux fork
execution path have not been run. No claim is made that a currently checked-in
Dash source file already triggers the problem. The direct caller in
test/lint/lint-circular-dependencies.py consumes this tool's output, so missed
edges can affect its cycle detection; a full CI run was not simulated.
A bounded search did not find this exact trigger already reported, but this is
not proof that no duplicate exists. No accepted fix, confirmed bounty eligibility, or payment is claimed.
Supporting files are attached as dash-core-circular-dependency-report-v2.zip.
This issue reports the technical defect only; it does not submit a bounty claim or request payment.
dash-core-circular-dependency-report-v2.zip