cmcfarlen commented on PR #13352:
URL: https://github.com/apache/trafficserver/pull/13352#issuecomment-5220360869

   The core diagnosis is right and I verified the pieces it rests on. 
`PCRE2_INFO_CAPTURECOUNT` does report the groups the pattern *defines* 
(excluding group 0), so validating `$N` against `Regex::get_capture_count()` at 
compile time is the correct bound, and `_tokens[i] > captureCount` is the right 
comparison — `$3` against a 3-group pattern is legal, `$4` is not. Moving that 
check out of the per-match path into `compile()` is the real fix: the old code 
was comparing a config-time constant against a per-request value, which is why 
a request without a query string could invalidate a `$3` that is perfectly 
valid for the pattern.
   
   The hardening is solid too. `std::errc{} == ec && parsed == end` is the 
right `from_chars()` idiom — it rejects trailing junk and out-of-range in one 
call, which a `strtoul`-style check usually misses. And 
`shouldReportEmptyPath()` via `_reportedEmptyPath.exchange(true, relaxed)` 
correctly makes the empty-path complaint once per instance rather than per 
transaction, which matters here precisely because whether the replacement 
collapses is request-dependent.
   
   **One thing I think is still open, and it is an ordering hazard with 
#13441.**
   
   The null-pointer guard covers only one of the two ways `dst` can end up 
empty:
   
   ```cpp
   std::string_view dst = (replIndex < matchCount) ? matches[replIndex] : 
std::string_view{""};
   ```
   
   The `""` protects the `replIndex >= matchCount` branch — the 
trailing-optional-group case this PR is about. But your own PCRE2 analysis in 
the thread above establishes the other case: for `(a)?(b)` on subject `b`, 
`matchCount` is 3 and group 1 is `PCRE2_UNSET`, so `replIndex < matchCount` 
holds and the *first* branch is taken.
   
   Today that yields a garbage pointer with zero length. Once #13441 lands, 
`RegexMatches::operator[]` returns `std::string_view()` for an unset group — 
its unit test asserts `matches[1].data() == nullptr` explicitly. So after 
#13441, this line hands a genuine `nullptr` to:
   
   ```cpp
   PrefetchDebug("replacing '$%d' with '%.*s'", replIndex, 
static_cast<int>(dst.length()), dst.data());
   result.append(dst);
   ```
   
   which is the exact `%.*s`-with-null concern raised at pattern.cc:186/198. 
The reply there says the view is now built from `""` so `data()` is never null; 
that holds for the branch it was aimed at, but not for this one. Benign on the 
libcs we build against, still undefined, and #13441 turns it from "invalid 
pointer" into "null pointer" — the case implementations are least likely to 
tolerate.
   
   It is reachable with an ordinary pattern, not just a contrived one: any 
optional group that precedes a participating group, e.g. 
`--fetch-path-pattern=/(v\d+/)?(.*-)(\d+)$/$1$2{$3+1}/` on a request without 
the version prefix.
   
   Normalizing regardless of which branch produced the view closes it and makes 
the guard say what the comment already claims:
   
   ```cpp
   std::string_view dst = (replIndex < matchCount) ? matches[replIndex] : 
std::string_view{};
   if (nullptr == dst.data()) {
     dst = std::string_view{""};
   }
   ```
   
   That also means this PR stops depending on #13441's choice of return value 
either way, which seems worth having given both are in flight — right now the 
correctness of this line is coupled to a decision made in another PR.
   
   Two smaller notes:
   
   - Dropping `Pattern::process()` and `Pattern::capture()` is fine — I 
confirmed no remaining callers. Worth keeping in the description since removing 
public-looking plugin methods tends to raise eyebrows.
   - The "refuse to load on invalid config" change is the right call and 
matching `--fetch-policy`'s existing behavior is a good argument, but it is a 
genuine behavior change: an operator whose `--fetch-count` was silently ignored 
now fails the remap. You already flagged it and offered to split; I would keep 
it here, since shipping the validation without the enforcement would leave the 
bad config running. Just make sure it lands in a release note.
   
   Nothing else from me — with the null normalization I would be happy with 
this.
   


-- 
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