Skip to content

strcspn() odd behaviour with NUL bytes and empty mask #12592

Closed
@nielsdos

Description

@nielsdos

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions