| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
Fix off-by-one read beyond the end of a string.
| } | ||
| /* miss: check if next character is part of pattern */ | ||
| if (!STRINGLIB_BLOOM(mask, ss[i+1])) { | ||
| if (i < w && !STRINGLIB_BLOOM(mask, ss[i+1])) { |
There was a problem hiding this comment.
I'm totally not sure that else branch is appropiate here for 'i == w2.
Even since we have i > w for this m this logic isn't clear for me.
What about simple replacement i <= w for loop condition with classical i < w?
It could be enough and cleaner.
Sorry, something went wrong.
There was a problem hiding this comment.
I think the else branch will be OK, since all it does is advance the index, and it is intended and expected that this could potentially advance it past the end of the string, in which case the for loop will terminate.
We can't just replace the the loop condition with i < w since the w here is the last valid index that the pattern could appear at, and needs to be checked. Otherwise we would miss valid matches, and indeed such a change breaks a large number of unit tests.
Note the conditionals that were changed are miss conditions, i.e. the algorithm has determined the character at the index cannot be part of the pattern at this location in the string. The conditionals modified are checking whether the following character could potentially be part of a pattern hit, so as to determine whether to skip it entirely by advancing the full length of the pattern or only as much as possible while still considering it as a valid potential hit. In the case where we are at the end of the buffer it doesn't actually matter which branch we take, since either way it will advance past it and terminate. We just need to avoid reading the invalid following character when it doesn't exist.
Sorry, something went wrong.
There was a problem hiding this comment.
Maybe it's better to rewrite condition as i+1 <= w? It seems to be more obvious way of checking for me.
It's very similar to "for" condition, but for another argument and before direct using of i+1 as index.
... the w here is the last valid index that the pattern could appear at
IMO, rewrited condition slightly reduces cognitive load.
Sorry, something went wrong.
|
I'm not at all an expert in this code, so I've tried to apply your patch by myself. But I suggest some changes. |
Sorry, something went wrong.
I have been hesitant to ping specific developers, as I don't want to annoy people or violate etiquette, but if you think it would be appropriate there are a couple of devs who have worked on this code frequently and/or recently I could tag in?
That is an excellent find, thanks! Agreed: I'll investigate, try to get a test that reproduces the issue there also, and fix it. |
Sorry, something went wrong.
|
I've just used git blame and find some names. |
Sorry, something went wrong.
|
I can trigger the off-by-one reads in the adaptive algorithm easily enough, but I haven't been able to get it to cause an ASAN error as yet, so no luck so far on a test for that. The third variant, default_rfind, already includes the equivalent guard: /* miss: check if previous character is part of pattern */
if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
i = i - m;
}
else {
i = i - skip;
}
}
else {
/* skip: check if previous character is part of pattern */
if (i > 0 && !STRINGLIB_BLOOM(mask, s[i-1])) {
i = i - m;
}
}
} |
Sorry, something went wrong.
|
Does default_rfind have tests on it? If not, those worth to be added too. IMO, we can add check and tests for adaptive_find even if there is no real crash under ASan. |
Sorry, something went wrong.
|
default_rfind does have tests, including ones that exercise the relevant code path. In fact any str.rfind with a non-matching pattern longer than one character (but shorter or equal to input length so it doesn't short-circuit) does so. adaptive_find also has tests, albeit none that trigger the code branches in question. Looking closer at adaptive_find, on second thought I don't think the check is strictly required there. adaptive_find is only ever called directly from FASTSEARCH. For that function to use the adaptive_find algorithm the input length has to be > 2.5k length, and the pattern length has to be more than one character (actually 6+ or 100+ depending on input size). Given that, the character "one past the last possible position the pattern could start" will always be a valid character in the input string. As mentioned above, it doesn't actually matter which branch is taken, so it doesn't matter what its value is. Presumably the extra clause in the conditional has some measurable performance impact, however slight, and given the input string minimum length requirements it will likely add up to much more than would be saved by avoiding one unnecessary character read. So, perhaps we shouldn't add it to adaptive_find, even though the current code seems safe only due to unchecked implementation details of when/under what circumstances it is called. Nonetheless, I'll push out an update to this that adds an adaptive_find test, to ensure that code path is exercised, even though there is nothing we can check to verify it is doing what it is supposed to. |
Sorry, something went wrong.
…asons for and limitations of the tests.
|
I suggest to slightly rewrite i < w condition as i + 1 <= w for default_find and maybe i > 0 as i - 1 >= 0 for default_rfind. But those are only cosmetical changes, in other aspects LGTM. |
Sorry, something went wrong.
…mment explaining why a guard is not necessary in adaptive_find.
Sure thing, will update, thanks! |
Sorry, something went wrong.
There was a problem hiding this comment.
LGTM. I'd like to have just little rewording in the comment.
Sorry, something went wrong.
There was a problem hiding this comment.
I am not fond of tests that may trigger a crash and the comments explaining why we have them are too long IMO. I don't see why we can't find an explicit input that triggers the code paths we want (except that it may be hard to craft such input). If it's too hard, we can just.. remove the tests. Or we should indicate in the C code that tests must be updated if we change the implementation.
Sorry, something went wrong.
I'm not fond of such tests either. I was unable to craft a test case that triggers an ASAN warning, so I will gladly drop it. |
Sorry, something went wrong.
comments, drop extremely marginal "test"
|
Ok, just remove the ASAN guard (my bad). |
Sorry, something went wrong.
There was a problem hiding this comment.
After removing the ASAN guard, I'll merge. Note that default_rfind actually has the checks for i > 0, so it does make sense to keep them for find as well.
Sorry, something went wrong.
|
OK, will remove the ASAN guard and always run the test. I agree, that seems easiest. Thanks very much for the reviews, both of you, I appreciate it! |
Sorry, something went wrong.
|
Sorry, @duaneg and @picnixz, I could not cleanly backport this to 3.13 due to a conflict. cherry_picker 85ec3b3b503ffd5b7e45f8b3fa2cec0c10e4bef0 3.13 |
Sorry, something went wrong.
…g search (pythonGH-132574) (cherry picked from commit 85ec3b3) Co-authored-by: Duane Griffin <duaneg@dghda.com>
|
Can you take care of the failed backport? TiA. |
Sorry, something went wrong.
|
GH-136628 is a backport of this pull request to the 3.14 branch. |
Sorry, something went wrong.
Sorry, something went wrong.
|
IMO, it'll be better to wait few days for @duaneg's response. I also can make such backport. |
Sorry, something went wrong.
|
With pleasure! It's just so that I can close the issue otherwise I'll likely forget about it :) |
Sorry, something went wrong.
|
Sorry, is there anything else to do? It looks like the backport PR has now been merged (thanks!) but if there is anything more required, please let me know and I'll take care of it. Apologies for not doing it myself immediately last night, but it was 1am local time and I've not done a (CPython) backport before, so while it all looks quite straight-forward I wanted to wait until morning when I had time and was fully awake 🙂 |
Sorry, something went wrong.
|
Yes, there is some work to be done. Also, this link maybe useful: https://devguide.python.org/contrib/core-team/committing/#backporting-changes-to-an-older-version |
Sorry, something went wrong.
|
GH-136645 is a backport of this pull request to the 3.13 branch. |
Sorry, something went wrong.
|
GH-136648 is a backport of this pull request to the 3.13 branch. |
Sorry, something went wrong.
This first one I made a mistake and tried to merge it into main, sorry. Hopefully the second attempt worked correctly! |
Sorry, something went wrong.
…g during search (pythonGH-132574) (python#136628) pythongh-127971: fix off-by-one read beyond the end of a string during search (pythonGH-132574) (cherry picked from commit 85ec3b3) Co-authored-by: Duane Griffin <duaneg@dghda.com>
| Back | FazBrowse Home | New Git URL |
Fix cases where the string search algorithm reads one past the end of the character/byte array under certain conditions.