brbzull0 opened a new pull request, #13634:
URL: https://github.com/apache/trafficserver/pull/13634

   `RecLookupMatchingRecords()` compiles a caller-supplied pattern and runs it
   against every record and metric name with no complexity bound. It backs the
   `admin_lookup_records` JSONRPC method, which is served inline on the RPC
   server's single thread, so a pattern that backtracks heavily stalls that 
thread
   for the duration.
   
   Measured on a stock server carrying 1328 names: roughly 19s of CPU per 
request
   unbounded, against about 123ms with the limit in this change.
   
   ### Change
   
   - `include/tsutil/Regex.h`: add `RE_ERROR_MATCHLIMIT` (-47) to `REErrors`,
     alongside the existing `RE_ERROR_NOMATCH` and `RE_ERROR_NULL`.
   - `src/tsutil/Regex.cc`: add the matching `static_assert` against
     `PCRE2_ERROR_MATCHLIMIT`, following the pattern already used for the other 
two.
   - `src/records/RecCore.cc`: give all four `regex.exec()` call sites in
     `RecLookupMatchingRecords()` a `RegexMatchContext` with a 50,000 step match
     limit.
   
   Exhausting the limit is not a match verdict, so those names are counted and
   reported once per lookup via `Warning()` rather than silently dropped from 
the
   caller's results. Any other negative return keeps the existing
   treat-as-no-match behaviour. The pattern itself is deliberately not echoed 
into
   the log.
   
   On the choice of 50,000: raising the limit costs run time linearly, while the
   longest name a pattern can still resolve grows only as its square root, since
   proving no match costs about L^2/2 steps. 50,000 resolves names up to ~310
   characters, 4.7x the 66 character longest name a stock build registers. The 
1750
   used elsewhere in the tree reaches only ~59 characters and cannot resolve 
what
   ships.
   
   ### Tests
   
   **Unit** -- `src/records/unit_tests/test_RecLookupMatchingRecords.cc`, three
   cases covering config records, metrics (counter and static string) and hidden
   metrics. `test_records` goes from 317 assertions in 44 cases on master to 
636 in
   49.
   
   **Autest** -- 
`tests/gold_tests/traffic_ctl/traffic_ctl_config_match_limit.test.py`
   drives `traffic_ctl config match` with `^([a-z.]+)+[~=]` and asserts three
   things: the command returns promptly with no matches, an ordinary lookup
   afterwards still answers (so the RPC server was not left wedged), and the 
diags
   log carries the incomplete-results warning rather than silently returning a
   short answer.
   
   Two details in that pattern are load bearing, and are commented in the test. 
The
   tail must be a character class rather than a literal, because for an absent
   literal PCRE2's required-code-unit check rejects each name up front and no
   backtracking happens -- the test would then pass with or without the change. 
And
   it must be unsatisfiable rather than merely unlikely: an earlier `[0-9]\z` 
tail
   matched `proxy.config.ssl.TLSv1`, since lookups are case insensitive. The
   commonly cited `(a+)+$` is useless here, as no real record name holds two
   consecutive `a` characters to backtrack over.
   
   Asserting on the warning rather than on elapsed time keeps the test 
independent
   of machine speed.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to