Description
Description
The following code:
<?php
var_dump(strcspn("abc\0def", " "));
var_dump(strcspn("abc\0def", ""));
Resulted in this output:
int(7)
int(3)
But I expected this output instead:
int(7)
int(7)
Looking at the description of strcspn:
Returns the length of the initial segment of string which does not contain any of the characters in characters.
IOW, if the mask is empty, the stop condition is never met so the total length of the string must be returned, no?
I discovered this while I was working on an optimization for this function similar to what I did for strspn.
This has been wrong since 1999: a15916a
There are actually tests that check this behaviour, but I think that's just incidental as the test date from way later than 1999.
The reason this happens is because php_strcspn
accesses *s2
even if it is empty, thus reading a NUL byte out of bounds and thinking that the NUL is part of the mask.
Fixing this is trivial however, although I wouldn't recommend doing this in stable branches as it is a behaviour change, although a weird one:
diff --git a/ext/standard/string.c b/ext/standard/string.c
index 8beedfe818..b5deb20403 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -1640,16 +1640,16 @@ PHPAPI size_t php_strcspn(const char *s1, const char *s2, const char *s1_end, co
const char *p, *spanp;
char c = *s1;
- for (p = s1;;) {
- spanp = s2;
- do {
- if (*spanp == c || p == s1_end) {
+ for (p = s1; p < s1_end;) {
+ for (spanp = s2; spanp < s2_end; spanp++) {
+ if (*spanp == c) {
return p - s1;
}
- } while (spanp++ < (s2_end - 1));
+ }
c = *++p;
}
- /* NOTREACHED */
+
+ return p - s1;
}
/* }}} */
I'd say: let's fix it only in master?
PHP Version
Every version since 1999
Operating System
No response