Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gdbinit
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ define print_cvs

printf "Compiled variables count: %d\n\n", $cv_count
while $cv_idx < $cv_count
printf "[%d] '%s'\n", $cv_idx, $cv[$cv_idx].val
printf "[%d] '$%s'\n", $cv_idx, $cv[$cv_idx].val@$cv[$cv_idx].len
set $zvalue = ((zval *) $cv_ex_ptr) + $callFrameSize + $cv_idx
printzv $zvalue
set $cv_idx = $cv_idx + 1
Expand Down
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ PHP NEWS
with a given skeleton, locale, collapse type and identity fallback.
(BogdanUngureanu)

- Standard:
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
while COW violation flag is still set). (alexandre-daubois)

<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
8 changes: 6 additions & 2 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -3489,6 +3489,12 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
HT_SET_ITERATORS_COUNT(in_hash, 0);
in_hash->pDestructor = NULL;

/* Set internal pointer to 0 directly instead of calling zend_hash_internal_pointer_reset().
* This avoids the COW violation assertion and delays advancing to the first valid position
* until after we've switched to the new array structure (out_hash). The iterator will be
* advanced when actually accessed, at which point it will find valid indexes in the new array. */
in_hash->nInternalPointer = 0;

if (UNEXPECTED(GC_DELREF(in_hash) == 0)) {
/* Array was completely deallocated during the operation */
zend_array_destroy(in_hash);
Expand All @@ -3507,8 +3513,6 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H
in_hash->nNextFreeElement = out_hash.nNextFreeElement;
in_hash->arData = out_hash.arData;
in_hash->pDestructor = out_hash.pDestructor;

zend_hash_internal_pointer_reset(in_hash);
}
/* }}} */

Expand Down
20 changes: 20 additions & 0 deletions ext/standard/tests/array/gh19926.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
GH-19926 (Assertion failure zend_hash_internal_pointer_reset_ex)
--FILE--
<?php
class ThrowingDestructor {
function __destruct() {
throw new Exception();
}
}

$arr = [new ThrowingDestructor(), new ThrowingDestructor()];

try {
array_splice($arr, 0, 2);
} catch (Exception $e) {
echo "Exception caught, no assertion failure\n";
}
?>
--EXPECT--
Exception caught, no assertion failure
19 changes: 19 additions & 0 deletions ext/standard/tests/array/gh19926_pointer.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
GH-19926 (internal pointer behavior after array_splice)
--FILE--
<?php
$a = [1, 2, 3];
unset($a[0]);
next($a);

echo "Before array_splice: ";
var_dump(current($a));

array_splice($a, 0, 0, [999]);

echo "After array_splice: ";
var_dump(current($a));
?>
--EXPECT--
Before array_splice: int(3)
After array_splice: int(999)
22 changes: 22 additions & 0 deletions ext/standard/tests/streams/gh19570.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
GH-19570 (unable to fseek in /dev/zero and /dev/null)
--SKIPIF--
<?php
if (PHP_OS_FAMILY !== "Linux") die("skip only for Linux");
?>
--FILE--
<?php
$devices = [
// Note: could test others but they're not necessarily available
"/dev/zero",
"/dev/null",
"/dev/full",
];
foreach ($devices as $device) {
var_dump(fseek(fopen($device, "rb"), 1*1024*1024, SEEK_SET));
}
?>
--EXPECT--
int(0)
int(0)
int(0)
2 changes: 1 addition & 1 deletion ext/uri/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ if test "$PHP_EXTERNAL_URIPARSER" = "no"; then
$URIPARSER_DIR/src/UriSetScheme.c $URIPARSER_DIR/src/UriSetUserInfo.c $URIPARSER_DIR/src/UriShorten.c $URIPARSER_DIR/src/UriVersion.c"
URI_CFLAGS="-DURI_STATIC_BUILD"
else
PKG_CHECK_MODULES([LIBURIPARSER], [liburiparser >= 0.9.9])
PKG_CHECK_MODULES([LIBURIPARSER], [liburiparser >= 0.9.10])
PHP_EVAL_LIBLINE([$LIBURIPARSER_LIBS], [URI_SHARED_LIBADD])
PHP_EVAL_INCLINE([$LIBURIPARSER_CFLAGS])
fi
Expand Down
6 changes: 3 additions & 3 deletions ext/uri/uriparser/include/uriparser/Uri.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ typedef struct URI_TYPE(HostDataStruct) {
URI_TYPE(TextRange) ipFuture; /**< IPvFuture address
@note
With non-<c>NULL</c> members in UriUriStructA.hostData context,
this text range's pointers must be <em>idential</em> to those
this text range's pointers must be <em>identical</em> to those
of UriUriStructA.hostText at all times. */
} URI_TYPE(HostData); /**< @copydoc UriHostDataStructA */

Expand Down Expand Up @@ -1420,7 +1420,7 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, con
*
* @param first <b>IN</b>: Pointer to first character
* @param afterLast <b>IN</b>: Pointer to character after the last one still in
* @param hasHost <b>IN</b>: Wether the target %URI has a non-<c>NULL</c> host set or not
* @param hasHost <b>IN</b>: Whether the target %URI has a non-<c>NULL</c> host set or not
* @return <c>URI_TRUE</c> if non-<c>NULL</c> and well-formed, else <c>URI_FALSE</c>
*
* @see uriIsWellFormedFragmentA
Expand Down Expand Up @@ -2415,7 +2415,7 @@ URI_PUBLIC int URI_FUNC(SetUserInfoMm)(URI_TYPE(Uri) * uri,
* or even fully custom patches. As a result, the version string
* returned serves as nothing more than "based on that version",
* it does not guarantee equivalence to vanilla upstream releases
* or absence of additinal downstream patches.
* or absence of additional downstream patches.
* It is nothing more than "a hint" and MUST NEVER be used to
* make decisions on in application code at runtime.
*
Expand Down
80 changes: 60 additions & 20 deletions ext/uri/uriparser/include/uriparser/UriBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ typedef struct UriIp6Struct {
} UriIp6; /**< @copydoc UriIp6Struct */


struct UriMemoryManagerStruct; /* foward declaration to break loop */
struct UriMemoryManagerStruct; /* forward declaration to break loop */


/**
Expand Down Expand Up @@ -287,28 +287,34 @@ typedef enum UriResolutionOptionsEnum {


/**
* Wraps a memory manager backend that only provides malloc and free
* to make a complete memory manager ready to be used.
* Wraps a memory manager backend that only provides <c>malloc(3)</c> and
* <c>free(3)</c> to make a complete memory manager ready to be used.
*
* The core feature of this wrapper is that you don't need to implement
* realloc if you don't want to. The wrapped memory manager uses
* backend->malloc, memcpy, and backend->free and soieof(size_t) extra
* bytes per allocation to emulate fallback realloc for you.
* <c>realloc(3)</c> if you don't want to. The wrapped memory manager uses
* <c>backend-&gt;malloc</c>, <c>memcpy(3)</c>, and <c>backend-&gt;free</c> and
* (at least) <c>sizeof(size_t)</c> extra bytes per allocation to emulate
* fallback <c>realloc(3)</c> for you.
*
* memory->calloc is uriEmulateCalloc.
* memory->free uses backend->free and handles the size header.
* memory->malloc uses backend->malloc and adds a size header.
* memory->realloc uses memory->malloc, memcpy, and memory->free and reads
* the size header.
* memory->reallocarray is uriEmulateReallocarray.
* <ul>
* <li><c>memory-&gt;calloc</c> is <c>uriEmulateCalloc</c>.</li>
* <li><c>memory-&gt;free</c> uses <c>backend-&gt;free</c>,
* and handles the size header.</li>
* <li><c>memory-&gt;malloc</c> uses <c>backend-&gt;malloc</c>,
* and adds a size header.</li>
* <li><c>memory-&gt;realloc</c> uses <c>memory-&gt;malloc</c>,
* <c>memcpy(3)</c> and <c>memory-&gt;free</c>,
* and reads the size header.</li>
* <li><c>memory-&gt;reallocarray</c> is <c>uriEmulateReallocarray</c>.</li>
* </ul>
*
* The internal workings behind memory->free, memory->malloc, and
* memory->realloc may change so the functions exposed by these function
* pointer sshould be consided internal and not public API.
* The internal workings behind <c>memory-&gt;free</c>, <c>memory-&gt;malloc</c>,
* and <c>memory-&gt;realloc</c> may change, and the functions exposed by these
* function pointers should be considered internal and not public API.
*
* @param memory <b>OUT</b>: Where to write the wrapped memory manager to
* @param backend <b>IN</b>: Memory manager to use as a backend
* @return Error code or 0 on success
* @return Error code or 0 on success
*
* @see uriEmulateCalloc
* @see uriEmulateReallocarray
Expand All @@ -321,7 +327,7 @@ URI_PUBLIC int uriCompleteMemoryManager(UriMemoryManager * memory,


/**
* Offers emulation of calloc(3) based on memory->malloc and memset.
* Offers emulation of calloc(3) based on memory-&gt;malloc and memset.
* See "man 3 calloc" as well.
*
* @param memory <b>IN</b>: Memory manager to use, should not be NULL
Expand All @@ -340,11 +346,11 @@ URI_PUBLIC void * uriEmulateCalloc(UriMemoryManager * memory,


/**
* Offers emulation of reallocarray(3) based on memory->realloc.
* Offers emulation of reallocarray(3) based on memory-&gt;realloc.
* See "man 3 reallocarray" as well.
*
* @param memory <b>IN</b>: Memory manager to use, should not be NULL
* @param ptr <b>IN</b>: Pointer allocated using memory->malloc/... or NULL
* @param ptr <b>IN</b>: Pointer allocated using memory-&gt;malloc/... or NULL
* @param nmemb <b>IN</b>: Number of elements to allocate
* @param size <b>IN</b>: Size in bytes per element
* @return Pointer to allocated memory or NULL
Expand All @@ -369,18 +375,52 @@ URI_PUBLIC void * uriEmulateReallocarray(UriMemoryManager * memory,
* 5. and frees that memory.
*
* It is recommended to compile with AddressSanitizer enabled
* to take full advantage of uriTestMemoryManager.
* to take full advantage of <c>uriTestMemoryManager</c>.
*
* For backwards-compatibility, <c>uriTestMemoryManager</c>
* does not challenge pointer alignment; please see
* <c>uriTestMemoryManagerEx</c> for that feature.
*
* @param memory <b>IN</b>: Memory manager to use, should not be NULL
* @return Error code or 0 on success
*
* @see uriEmulateCalloc
* @see uriEmulateReallocarray
* @see UriMemoryManager
* @see uriTestMemoryManagerEx
* @since 0.9.0
*/
URI_PUBLIC int uriTestMemoryManager(UriMemoryManager * memory);



/**
* Run multiple tests against a given memory manager.
* For example, one test
* 1. allocates a small amount of memory,
* 2. writes some magic bytes to it,
* 3. reallocates it,
* 4. checks that previous values are still present,
* 5. and frees that memory.
*
* It is recommended to compile with both AddressSanitizer and
* UndefinedBehaviorSanitizer enabled to take full advantage of
* <c>uriTestMemoryManagerEx</c>. Note that environment variable
* <c>UBSAN_OPTIONS</c> may need adjustment to make UndefinedBehaviorSanitizer
* fatal (which by default it is not).
*
* @param memory <b>IN</b>: Memory manager to use, should not be NULL
* @param challengeAlignment <b>IN</b>: Whether to challenge pointer alignment
* @return Error code or 0 on success
*
* @see uriEmulateCalloc
* @see uriEmulateReallocarray
* @see UriMemoryManager
* @see uriTestMemoryManager
* @since 0.9.10
*/
URI_PUBLIC int uriTestMemoryManagerEx(UriMemoryManager * memory, UriBool challengeAlignment);



#endif /* URI_BASE_H */
31 changes: 21 additions & 10 deletions ext/uri/uriparser/src/UriCommon.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,25 @@ UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri,
*
* For example, changing "./http://foo" into "http://foo" would change semantics
* and hence the dot segment is essential to that case and cannot be removed.
*
* Other examples that would change semantics are:
* - cutting "/.//" down to "//"
* - cutting "scheme:/.//" down to "scheme://".
*/
removeSegment = URI_TRUE;
if (relative && (walker == uri->pathHead) && (walker->next != NULL)) {
const URI_CHAR * ch = walker->next->text.first;
for (; ch < walker->next->text.afterLast; ch++) {
if (*ch == _UT(':')) {
removeSegment = URI_FALSE;
break;
if ((walker == uri->pathHead) && (walker->next != NULL)) {
/* Detect case "/.//" (with or without scheme) */
if ((walker->next->text.first == walker->next->text.afterLast)
&& (URI_FUNC(HasHost)(uri) == URI_FALSE)) {
removeSegment = URI_FALSE;
/* Detect case "./withcolon:" */
} else if (relative) {
const URI_CHAR * ch = walker->next->text.first;
for (; ch < walker->next->text.afterLast; ch++) {
if (*ch == _UT(':')) {
removeSegment = URI_FALSE;
break;
}
}
}
}
Expand Down Expand Up @@ -358,7 +369,7 @@ UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri,
}
memory->free(memory, walker);
} else {
/* Re-use segment for "" path segment to represent trailing slash, update tail */
/* Reuse segment for "" path segment to represent trailing slash, update tail */
URI_TYPE(PathSegment) * const segment = walker;
if (pathOwned && (segment->text.first != segment->text.afterLast)) {
memory->free(memory, (URI_CHAR *)segment->text.first);
Expand Down Expand Up @@ -403,7 +414,7 @@ UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri,
* NEW: tail -> NULL */
uri->pathTail = NULL;
} else {
/* Re-use segment for "" path segment to represent trailing slash,
/* Reuse segment for "" path segment to represent trailing slash,
* then update head and tail */
if (pathOwned && (walker->text.first != walker->text.afterLast)) {
memory->free(memory, (URI_CHAR *)walker->text.first);
Expand Down Expand Up @@ -696,7 +707,7 @@ static UriBool URI_FUNC(PrependNewDotSegment)(URI_TYPE(Uri) * uri, UriMemoryMana
/* When dropping a scheme from a URI without a host and with a colon (":")
* in the first path segment, a consecutive reparse would rightfully
* mis-classify the first path segment as a scheme due to the colon.
* To protect against this case, we prepend an artifical "." segment
* To protect against this case, we prepend an artificial "." segment
* to the path in here; the function is called after the scheme has
* just been dropped.
*
Expand Down Expand Up @@ -751,7 +762,7 @@ UriBool URI_FUNC(FixPathNoScheme)(URI_TYPE(Uri) * uri,
/* When dropping a host from a URI without a scheme, an absolute path
* and and empty first path segment, a consecutive reparse would rightfully
* mis-classify the first path segment as a host marker due to the "//".
* To protect against this case, we prepend an artifical "." segment
* To protect against this case, we prepend an artificial "." segment
* to the path in here; the function is called after the host has
* just been dropped.
*
Expand Down
Loading