From 64c1d43b68dd812ce4d3dec0a9b469fd4c060f30 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <2144837+alexandre-daubois@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:51:23 +0200 Subject: [PATCH 1/6] Fix GH-19926: reset internal pointer earlier while splicing array while COW violation flag is still set (#19929) --- NEWS | 2 ++ ext/standard/array.c | 8 ++++++-- ext/standard/tests/array/gh19926.phpt | 20 +++++++++++++++++++ ext/standard/tests/array/gh19926_pointer.phpt | 19 ++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 ext/standard/tests/array/gh19926.phpt create mode 100644 ext/standard/tests/array/gh19926_pointer.phpt diff --git a/NEWS b/NEWS index 2edaf09ce70e..c2db743c84e0 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,8 @@ PHP NEWS (alexandre-daubois) . Fixed bug GH-20043 (array_unique assertion failure with RC1 array causing an exception on sort). (nielsdos) + . Fixed bug GH-19926 (reset internal pointer earlier while splicing array + while COW violation flag is still set). (alexandre-daubois) - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). diff --git a/ext/standard/array.c b/ext/standard/array.c index b7b5f82d61c5..4896ac44a727 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3376,6 +3376,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); @@ -3394,8 +3400,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); } /* }}} */ diff --git a/ext/standard/tests/array/gh19926.phpt b/ext/standard/tests/array/gh19926.phpt new file mode 100644 index 000000000000..b714db9eb2b3 --- /dev/null +++ b/ext/standard/tests/array/gh19926.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-19926 (Assertion failure zend_hash_internal_pointer_reset_ex) +--FILE-- + +--EXPECT-- +Exception caught, no assertion failure diff --git a/ext/standard/tests/array/gh19926_pointer.phpt b/ext/standard/tests/array/gh19926_pointer.phpt new file mode 100644 index 000000000000..c134f3b594b3 --- /dev/null +++ b/ext/standard/tests/array/gh19926_pointer.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-19926 (internal pointer behavior after array_splice) +--FILE-- + +--EXPECT-- +Before array_splice: int(3) +After array_splice: int(999) From 467e05b13af71813f998c17075219270a20ecd0f Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 6 Oct 2025 16:57:28 +0200 Subject: [PATCH 2/6] Fix misplaced entry in NEWS --- NEWS | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index bfd460e74649..1b5783de1200 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ PHP NEWS . Fixed bug GH-19773 (SIGSEGV due to uninitialized soap_globals->lang_en). (nielsdos, KaseyJenkins) +- Standard: + . Fixed bug GH-19926 (reset internal pointer earlier while splicing array + while COW violation flag is still set). (alexandre-daubois) + - URI: . Fixed Uri\WhatWg\Url::withPort() when an invalid value is passed. (timwolla) @@ -103,8 +107,6 @@ PHP NEWS (alexandre-daubois) . Fixed GH-14402 (SplPriorityQueue, SplMinHeap, and SplMaxHeap lost their data on serialize()). (alexandre-daubois) - . Fixed bug GH-19926 (reset internal pointer earlier while splicing array - while COW violation flag is still set). (alexandre-daubois) - URI: . Fixed bug GH-19780 (InvalidUrlException should check $errors argument). From b7aeb0a69f94aeaa983c6dbba2c01b584fdfeabe Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 6 Sep 2025 14:20:24 +0200 Subject: [PATCH 3/6] Fix GH-19570: unable to fseek in /dev/zero and /dev/null On Linux, these two character devices are exceptions in that they can be seeked. Check their major/minor device number. Co-authored-by: divinity76 --- NEWS | 2 ++ ext/standard/tests/streams/gh19570.phpt | 22 ++++++++++++++++++++++ main/streams/plain_wrapper.c | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 ext/standard/tests/streams/gh19570.phpt diff --git a/NEWS b/NEWS index c2db743c84e0..7a42307caec4 100644 --- a/NEWS +++ b/NEWS @@ -64,6 +64,8 @@ PHP NEWS causing an exception on sort). (nielsdos) . Fixed bug GH-19926 (reset internal pointer earlier while splicing array while COW violation flag is still set). (alexandre-daubois) + . Fixed bug GH-19570 (unable to fseek in /dev/zero and /dev/null). + (nielsdos, divinity76) - Streams: . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). diff --git a/ext/standard/tests/streams/gh19570.phpt b/ext/standard/tests/streams/gh19570.phpt new file mode 100644 index 000000000000..e3d486ea83cf --- /dev/null +++ b/ext/standard/tests/streams/gh19570.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-19570 (unable to fseek in /dev/zero and /dev/null) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(0) +int(0) +int(0) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 9ac049f74684..09528e926f72 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -43,6 +43,10 @@ # include #endif +#ifdef __linux__ +# include +#endif + #define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC) #define php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC) #define php_stream_fopen_from_file_int(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC) @@ -255,7 +259,28 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC) static void detect_is_seekable(php_stdio_stream_data *self) { #if defined(S_ISFIFO) && defined(S_ISCHR) if (self->fd >= 0 && do_fstat(self, 0) == 0) { +#ifdef __linux__ + if (S_ISCHR(self->sb.st_mode)) { + /* Some character devices are exceptions, check their major/minor ID + * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt */ + if (major(self->sb.st_rdev) == 1) { + unsigned m = minor(self->sb.st_rdev); + self->is_seekable = + m == 1 || /* /dev/mem */ + m == 2 || /* /dev/kmem */ + m == 3 || /* /dev/null */ + m == 4 || /* /dev/port (seekable, offset = I/O port) */ + m == 5 || /* /dev/zero */ + m == 7; /* /dev/full */ + } else { + self->is_seekable = false; + } + } else { + self->is_seekable = !S_ISFIFO(self->sb.st_mode); + } +#else self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode)); +#endif self->is_pipe = S_ISFIFO(self->sb.st_mode); } #elif defined(PHP_WIN32) From 9d9b73a3f0d2fbfb60eee8e943e4135a73012f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 6 Oct 2025 20:17:53 +0200 Subject: [PATCH 4/6] .gdbinit: Fix printing of variable names in `print_cvs` (#20077) Previously only the first character of each variable was printed. --- .gdbinit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gdbinit b/.gdbinit index c4705b2f59a9..4477828265e0 100644 --- a/.gdbinit +++ b/.gdbinit @@ -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 From 08924cd8c4bf877fe1433561c43fcacf21acc0e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 6 Oct 2025 20:44:25 +0200 Subject: [PATCH 5/6] Regenerate main/debug_gdb_scripts.c Following 9d9b73a3f0d2fbfb60eee8e943e4135a73012f1a / GH-20077. --- main/debug_gdb_scripts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/debug_gdb_scripts.c b/main/debug_gdb_scripts.c index 384a1c8b46ee..032dd09491ba 100644 --- a/main/debug_gdb_scripts.c +++ b/main/debug_gdb_scripts.c @@ -55,7 +55,7 @@ asm( ".ascii \"\\n\"\n" ".ascii \" printf \\\"Compiled variables count: %d\\\\\\\\n\\\\\\\\n\\\", $cv_count\\n\"\n" ".ascii \" while $cv_idx < $cv_count\\n\"\n" - ".ascii \" printf \\\"[%d] \\\\'%s\\\\'\\\\\\\\n\\\", $cv_idx, $cv[$cv_idx].val\\n\"\n" + ".ascii \" printf \\\"[%d] \\\\'$%s\\\\'\\\\\\\\n\\\", $cv_idx, $cv[$cv_idx].val@$cv[$cv_idx].len\\n\"\n" ".ascii \" set $zvalue = ((zval *) $cv_ex_ptr) + $callFrameSize + $cv_idx\\n\"\n" ".ascii \" printzv $zvalue\\n\"\n" ".ascii \" set $cv_idx = $cv_idx + 1\\n\"\n" From 1ca78dd98dc58602d1ae1518d471d0682bd20204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 6 Oct 2025 21:51:13 +0200 Subject: [PATCH 6/6] uri: Update to uriparser-0.9.9-21-g08df3b2 (#19992) This is specifically to backport uriparser/uriparser#265. Fixes php/php-src#19897. --- NEWS | 2 + ext/uri/config.m4 | 2 +- ext/uri/uriparser/include/uriparser/Uri.h | 6 +- ext/uri/uriparser/include/uriparser/UriBase.h | 80 ++++++++++++++----- ext/uri/uriparser/src/UriCommon.c | 31 ++++--- ext/uri/uriparser/src/UriMemory.c | 47 ++++++++++- 6 files changed, 130 insertions(+), 38 deletions(-) diff --git a/NEWS b/NEWS index 1b5783de1200..ae3a252c7625 100644 --- a/NEWS +++ b/NEWS @@ -35,6 +35,8 @@ PHP NEWS (timwolla) . Fixed Uri\WhatWg\Url::parse() when resolving a relative URL against a base URL with query or fragment. (timwolla) + . Fixed normalization of paths starting with two slashes for + Uri\Rfc3986\Uri. (timwolla) 25 Sep 2025, PHP 8.5.0RC1 diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index c8a179c96ef2..99e0d6b47677 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -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 diff --git a/ext/uri/uriparser/include/uriparser/Uri.h b/ext/uri/uriparser/include/uriparser/Uri.h index 94441a2dc889..fbdb3d9a3798 100644 --- a/ext/uri/uriparser/include/uriparser/Uri.h +++ b/ext/uri/uriparser/include/uriparser/Uri.h @@ -137,7 +137,7 @@ typedef struct URI_TYPE(HostDataStruct) { URI_TYPE(TextRange) ipFuture; /**< IPvFuture address @note With non-NULL members in UriUriStructA.hostData context, - this text range's pointers must be idential to those + this text range's pointers must be identical to those of UriUriStructA.hostText at all times. */ } URI_TYPE(HostData); /**< @copydoc UriHostDataStructA */ @@ -1420,7 +1420,7 @@ URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, con * * @param first IN: Pointer to first character * @param afterLast IN: Pointer to character after the last one still in - * @param hasHost IN: Wether the target %URI has a non-NULL host set or not + * @param hasHost IN: Whether the target %URI has a non-NULL host set or not * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE * * @see uriIsWellFormedFragmentA @@ -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. * diff --git a/ext/uri/uriparser/include/uriparser/UriBase.h b/ext/uri/uriparser/include/uriparser/UriBase.h index 6b01bb79e9f8..92883278cff2 100644 --- a/ext/uri/uriparser/include/uriparser/UriBase.h +++ b/ext/uri/uriparser/include/uriparser/UriBase.h @@ -187,7 +187,7 @@ typedef struct UriIp6Struct { } UriIp6; /**< @copydoc UriIp6Struct */ -struct UriMemoryManagerStruct; /* foward declaration to break loop */ +struct UriMemoryManagerStruct; /* forward declaration to break loop */ /** @@ -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 malloc(3) and + * free(3) 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. + * realloc(3) if you don't want to. The wrapped memory manager uses + * backend->malloc, memcpy(3), and backend->free and + * (at least) sizeof(size_t) extra bytes per allocation to emulate + * fallback realloc(3) 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. + *
    + *
  • 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(3) and memory->free, + * and reads the size header.
  • + *
  • memory->reallocarray is uriEmulateReallocarray.
  • + *
* - * 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 memory->free, memory->malloc, + * and memory->realloc may change, and the functions exposed by these + * function pointers should be considered internal and not public API. * * @param memory OUT: Where to write the wrapped memory manager to * @param backend IN: 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 @@ -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->malloc and memset. * See "man 3 calloc" as well. * * @param memory IN: Memory manager to use, should not be NULL @@ -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->realloc. * See "man 3 reallocarray" as well. * * @param memory IN: Memory manager to use, should not be NULL - * @param ptr IN: Pointer allocated using memory->malloc/... or NULL + * @param ptr IN: Pointer allocated using memory->malloc/... or NULL * @param nmemb IN: Number of elements to allocate * @param size IN: Size in bytes per element * @return Pointer to allocated memory or NULL @@ -369,7 +375,11 @@ 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 uriTestMemoryManager. + * + * For backwards-compatibility, uriTestMemoryManager + * does not challenge pointer alignment; please see + * uriTestMemoryManagerEx for that feature. * * @param memory IN: Memory manager to use, should not be NULL * @return Error code or 0 on success @@ -377,10 +387,40 @@ URI_PUBLIC void * uriEmulateReallocarray(UriMemoryManager * memory, * @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 + * uriTestMemoryManagerEx. Note that environment variable + * UBSAN_OPTIONS may need adjustment to make UndefinedBehaviorSanitizer + * fatal (which by default it is not). + * + * @param memory IN: Memory manager to use, should not be NULL + * @param challengeAlignment IN: 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 */ diff --git a/ext/uri/uriparser/src/UriCommon.c b/ext/uri/uriparser/src/UriCommon.c index 2d4e947088bc..a644eec47536 100644 --- a/ext/uri/uriparser/src/UriCommon.c +++ b/ext/uri/uriparser/src/UriCommon.c @@ -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; + } } } } @@ -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); @@ -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); @@ -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. * @@ -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. * diff --git a/ext/uri/uriparser/src/UriMemory.c b/ext/uri/uriparser/src/UriMemory.c index 916d7cea8308..503b9f9787ff 100644 --- a/ext/uri/uriparser/src/UriMemory.c +++ b/ext/uri/uriparser/src/UriMemory.c @@ -64,6 +64,15 @@ +#define URI_MAX(a, b) (((a) > (b)) ? (a) : (b)) + +/* NOTE: This intends to mimic MALLOC_ALIGNMENT of glibc */ +#define URI_MALLOC_ALIGNMENT URI_MAX(2 * sizeof(size_t), sizeof(long double)) + +#define URI_MALLOC_PADDING (URI_MALLOC_ALIGNMENT - sizeof(size_t)) + + + #define URI_CHECK_ALLOC_OVERFLOW(total_size, nmemb, size) \ do { \ /* check for unsigned overflow */ \ @@ -170,7 +179,7 @@ void * uriEmulateReallocarray(UriMemoryManager * memory, static void * uriDecorateMalloc(UriMemoryManager * memory, size_t size) { UriMemoryManager * backend; - const size_t extraBytes = sizeof(size_t); + const size_t extraBytes = sizeof(size_t) + URI_MALLOC_PADDING; void * buffer; if (memory == NULL) { @@ -225,7 +234,7 @@ static void * uriDecorateRealloc(UriMemoryManager * memory, return NULL; } - prevSize = *((size_t *)((char *)ptr - sizeof(size_t))); + prevSize = *((size_t *)((char *)ptr - sizeof(size_t) - URI_MALLOC_PADDING)); /* Anything to do? */ if (size <= prevSize) { @@ -259,7 +268,7 @@ static void uriDecorateFree(UriMemoryManager * memory, void * ptr) { return; } - backend->free(backend, (char *)ptr - sizeof(size_t)); + backend->free(backend, (char *)ptr - sizeof(size_t) - URI_MALLOC_PADDING); } @@ -288,7 +297,7 @@ int uriCompleteMemoryManager(UriMemoryManager * memory, -int uriTestMemoryManager(UriMemoryManager * memory) { +int uriTestMemoryManagerEx(UriMemoryManager * memory, UriBool challengeAlignment) { const size_t mallocSize = 7; const size_t callocNmemb = 3; const size_t callocSize = 5; @@ -456,11 +465,41 @@ int uriTestMemoryManager(UriMemoryManager * memory) { buffer = NULL; } + /* challenge pointer alignment */ + if (challengeAlignment == URI_TRUE) { + long double * ptr = memory->malloc(memory, 4 * sizeof(long double)); + if (ptr != NULL) { + ptr[0] = 0.0L; + ptr[1] = 1.1L; + ptr[2] = 2.2L; + ptr[3] = 3.3L; + + { + long double * const ptrNew = memory->realloc(memory, ptr, 8 * sizeof(long double)); + if (ptrNew != NULL) { + ptr = ptrNew; + ptr[4] = 4.4L; + ptr[5] = 5.5L; + ptr[6] = 6.6L; + ptr[7] = 7.7L; + } + } + + memory->free(memory, ptr); + } + } + return URI_SUCCESS; } +int uriTestMemoryManager(UriMemoryManager * memory) { + return uriTestMemoryManagerEx(memory, /*challengeAlignment=*/ URI_FALSE); +} + + + /*extern*/ UriMemoryManager defaultMemoryManager = { uriDefaultMalloc, uriDefaultCalloc,