[packages/php-pecl-msgpack] Up to 3.0.1
arekm
arekm at pld-linux.org
Thu Oct 22 13:22:33 CEST 2026
commit d7f2a7ebf78042750206ef5e7bbc02a4df7c6333
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Thu Oct 22 13:22:06 2026 +0200
Up to 3.0.1
Fixes a segfault unserializing stale enum data (GH #186) and hardens the
unserializer against crafted input.
enum-unserialize-segfault.patch | 182 ++++++++++++++++++++++++++++++++++++++
php-pecl-msgpack.spec | 18 +++-
unserialize-crash-hardening.patch | 89 +++++++++++++++++++
3 files changed, 285 insertions(+), 4 deletions(-)
---
diff --git a/php-pecl-msgpack.spec b/php-pecl-msgpack.spec
index b142eda..c596cf4 100644
--- a/php-pecl-msgpack.spec
+++ b/php-pecl-msgpack.spec
@@ -9,12 +9,14 @@
Summary: PHP extension for interfacing with MessagePack
Name: %{php_name}-pecl-%{modname}
# For PHP < 7, see 0.5.x branch
-Version: 2.1.0
+Version: 3.0.1
Release: 1
License: PHP 3.01
Group: Development/Languages/PHP
Source0: https://pecl.php.net/get/%{modname}-%{version}.tgz
-# Source0-md5: 901960fda542dec313ab4d89bd63d06d
+# Source0-md5: d7cd8f5be5c30fe9a993d60fa985a7d8
+Patch0: enum-unserialize-segfault.patch
+Patch1: unserialize-crash-hardening.patch
URL: https://pecl.php.net/package/msgpack/
BuildRequires: %{php_name}-devel >= 4:7.0.0
BuildRequires: rpmbuild(macros) >= 1.666
@@ -49,8 +51,10 @@ These are the files needed to compile programs using MessagePack.
%prep
%setup -qc
mv %{modname}-%{version}/* .
+%patch -P0 -p1
+%patch -P1 -p1
-# https://github.com/msgpack/msgpack-php/issues/110
+# https://github.com/msgpack/msgpack-php/issues/110 (these self-skip on php >= 7.1)
rm tests/040*.phpt
%build
@@ -62,8 +66,10 @@ phpize
# simple module load test
%{__php} -n -q \
-d extension_dir=modules \
+%if "%php_major_version.%php_minor_version" < "7.4"
-d extension=%{php_extensiondir}/pcre.so \
-d extension=%{php_extensiondir}/spl.so \
+%endif
-d extension=%{php_extensiondir}/session.so \
-d extension=%{modname}.so \
-m > modules.log
@@ -74,7 +80,11 @@ cat <<'EOF' > run-tests.sh
export NO_INTERACTION=1 REPORT_EXIT_STATUS=1 MALLOC_CHECK_=2
exec %{__make} test \
PHP_EXECUTABLE=%{__php} \
+%if "%php_major_version.%php_minor_version" >= "7.4"
+ PHP_TEST_SHARED_SYSTEM_EXTENSIONS="session" \
+%else
PHP_TEST_SHARED_SYSTEM_EXTENSIONS="pcre spl session" \
+%endif
RUN_TESTS_SETTINGS="-q $*"
EOF
chmod +x run-tests.sh
@@ -107,7 +117,7 @@ fi
%files
%defattr(644,root,root,755)
-%doc CREDITS EXPERIMENTAL
+%doc CREDITS README.md
%config(noreplace) %verify(not md5 mtime size) %{php_sysconfdir}/conf.d/%{modname}.ini
%attr(755,root,root) %{php_extensiondir}/%{modname}.so
diff --git a/enum-unserialize-segfault.patch b/enum-unserialize-segfault.patch
new file mode 100644
index 0000000..f5e543f
--- /dev/null
+++ b/enum-unserialize-segfault.patch
@@ -0,0 +1,182 @@
+Fix segmentation fault when unserializing a nonexistent or non-case enum member.
+msgpack_unserialize() dereferenced the result of zend_enum_get_case() without
+checking the case exists, crashing on crafted or stale session data.
+
+Upstream: https://github.com/msgpack/msgpack-php/commit/b6e1202d78
+Upstream: https://github.com/msgpack/msgpack-php/commit/26c8c704b3
+
+diff --git a/msgpack_unpack.c b/msgpack_unpack.c
+index 88387e5..df33d7b 100644
+--- a/msgpack_unpack.c
++++ b/msgpack_unpack.c
+@@ -712,6 +712,19 @@ int msgpack_unserialize_map_item(msgpack_unpack_data *unpack, zval **container,
+ return 0;
+ }
+
++ /* found Enum does not contain specified case */
++ zend_class_constant *constant = zend_hash_find_ptr(
++ CE_CONSTANTS_TABLE(ce),
++ Z_STR_P(val));
++ if (constant == NULL || !(ZEND_CLASS_CONST_FLAGS(constant) & ZEND_CLASS_CONST_IS_CASE)) {
++ MSGPACK_WARNING(
++ "[msgpack] (%s) Enum case %s does not exist in Enum %s",
++ __FUNCTION__, Z_STRVAL_P(val), ZSTR_VAL(ce->name));
++
++ MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
++ return 0;
++ }
++
+ zend_object *enum_instance = zend_enum_get_case(ce, Z_STR_P(val));
+ ZVAL_OBJ_COPY(*container, enum_instance);
+ #endif
+diff --git a/tests/issue186.1.phpt b/tests/issue186.1.phpt
+new file mode 100644
+index 0000000..d93f1dd
+--- /dev/null
++++ b/tests/issue186.1.phpt
+@@ -0,0 +1,28 @@
++--TEST--
++Issue #182 (unknown enum case)
++--SKIPIF--
++<?php
++if (!extension_loaded("msgpack")) {
++ exit('skip because msgpack extension is missing');
++}
++if (version_compare(PHP_VERSION, '8.1.0', '<')) {
++ exit('skip Enum tests in PHP older than 8.1.0');
++}
++?>
++--FILE--
++Test
++<?php
++enum TestEnum
++{
++ case A;
++}
++
++$data = file_get_contents(__DIR__.'/issue186.ser.txt');
++$unserilized = msgpack_unserialize($data);
++?>
++OK
++--EXPECTF--
++Test
++
++Warning: [msgpack] (msgpack_unserialize_map_item) Enum case B does not exist in Enum TestEnum in %s/issue186.1.php on line 9
++OK
+diff --git a/tests/issue186.2.phpt b/tests/issue186.2.phpt
+new file mode 100644
+index 0000000..3e02e51
+--- /dev/null
++++ b/tests/issue186.2.phpt
+@@ -0,0 +1,30 @@
++--TEST--
++Issue #182 (unknown enum case)
++--SKIPIF--
++<?php
++if (!extension_loaded("msgpack")) {
++ exit('skip because msgpack extension is missing');
++}
++if (version_compare(PHP_VERSION, '8.1.0', '<')) {
++ exit('skip Enum tests in PHP older than 8.1.0');
++}
++?>
++--FILE--
++Test
++<?php
++enum TestEnum
++{
++ case A;
++
++ public const B = 42;
++}
++
++$data = file_get_contents(__DIR__.'/issue186.ser.txt');
++$unserilized = msgpack_unserialize($data);
++?>
++OK
++--EXPECTF--
++Test
++
++Warning: [msgpack] (msgpack_unserialize_map_item) Enum case B does not exist in Enum TestEnum in %s/issue186.2.php on line 11
++OK
+diff --git a/tests/issue186.3.phpt b/tests/issue186.3.phpt
+new file mode 100644
+index 0000000..610d8c8
+--- /dev/null
++++ b/tests/issue186.3.phpt
+@@ -0,0 +1,34 @@
++--TEST--
++Issue #182 (unknown enum case)
++--SKIPIF--
++<?php
++if (!extension_loaded("msgpack")) {
++ exit('skip because msgpack extension is missing');
++}
++if (version_compare(PHP_VERSION, '8.1.0', '<')) {
++ exit('skip Enum tests in PHP older than 8.1.0');
++}
++?>
++--FILE--
++Test
++<?php
++enum AnotherEnum {
++ case C;
++}
++
++enum TestEnum
++{
++ case A;
++
++ public const B = AnotherEnum::C;
++}
++
++$data = file_get_contents(__DIR__.'/issue186.ser.txt');
++$unserilized = msgpack_unserialize($data);
++?>
++OK
++--EXPECTF--
++Test
++
++Warning: [msgpack] (msgpack_unserialize_map_item) Enum case B does not exist in Enum TestEnum in %s/issue186.3.php on line 15
++OK
+diff --git a/tests/issue186.4.phpt b/tests/issue186.4.phpt
+new file mode 100644
+index 0000000..caac4bd
+--- /dev/null
++++ b/tests/issue186.4.phpt
+@@ -0,0 +1,27 @@
++--TEST--
++Issue #182 (unknown enum case)
++--SKIPIF--
++<?php
++if (!extension_loaded("msgpack")) {
++ exit('skip because msgpack extension is missing');
++}
++if (version_compare(PHP_VERSION, '8.1.0', '<')) {
++ exit('skip Enum tests in PHP older than 8.1.0');
++}
++?>
++--FILE--
++Test
++<?php
++enum TestEnum: string
++{
++ case A = 'a';
++ case B = 'b';
++}
++
++$data = file_get_contents(__DIR__.'/issue186.ser.txt');
++$unserilized = msgpack_unserialize($data);
++?>
++OK
++--EXPECTF--
++Test
++OK
+diff --git a/tests/issue186.ser.txt b/tests/issue186.ser.txt
+new file mode 100644
+index 0000000..130b76a
+--- /dev/null
++++ b/tests/issue186.ser.txt
+@@ -0,0 +1 @@
++���TestEnum�B
+\ No newline at end of file
diff --git a/unserialize-crash-hardening.patch b/unserialize-crash-hardening.patch
new file mode 100644
index 0000000..b158233
--- /dev/null
+++ b/unserialize-crash-hardening.patch
@@ -0,0 +1,89 @@
+Reject malformed unserialize input instead of crashing: a custom-object or enum
+record whose payload is not a string, and an object back-reference whose index is
+not an integer, were used without a type check; a back-reference that points at the
+container being built freed it before the copy. msgpack_unserialize() with a template
+dereferenced a null HashTable when data for an array/object property was a scalar or
+shorter than the template.
+
+diff --git a/msgpack_convert.c b/msgpack_convert.c
+index 1f90ef4..3e4148c 100644
+--- a/msgpack_convert.c
++++ b/msgpack_convert.c
+@@ -27,6 +27,13 @@ static inline int msgpack_convert_long_to_properties(HashTable *ht, zval *object
+ HashTable *dataht;
+ dataht = HASH_OF(val);
+
++ if (dataht == NULL) {
++ MSGPACK_WARNING("[msgpack] (%s) "
++ "data for an array property is not an array",
++ __FUNCTION__);
++ return FAILURE;
++ }
++
+ if ((dataval = zend_hash_index_find(dataht, prop_key_index)) == NULL) {
+ MSGPACK_WARNING("[msgpack] (%s) "
+ "can't get data value by index",
+@@ -401,7 +408,10 @@ int msgpack_convert_object(zval *return_value, zval *tpl, zval *value) /* {{{ */
+
+ num_key = 0;
+ ZEND_HASH_FOREACH_STR_KEY_VAL(ret, str_key, data) {
+- aryval = zend_hash_index_find(ht, num_key);
++ if ((aryval = zend_hash_index_find(ht, num_key)) == NULL) {
++ MSGPACK_WARNING("[msgpack] (%s) can't get data value by index", __FUNCTION__);
++ return FAILURE;
++ }
+
+ if (data == NULL) {
+ MSGPACK_WARNING("[msgpack] (%s) can't get data value by index", __FUNCTION__);
+diff --git a/msgpack_unpack.c b/msgpack_unpack.c
+index df33d7b..ad5bc0a 100644
+--- a/msgpack_unpack.c
++++ b/msgpack_unpack.c
+@@ -653,7 +653,7 @@ int msgpack_unserialize_map_item(msgpack_unpack_data *unpack, zval **container,
+ switch (type) {
+ case MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT:
+ {
+- if (Z_TYPE_P(key) != IS_STRING) {
++ if (Z_TYPE_P(key) != IS_STRING || Z_TYPE_P(val) != IS_STRING) {
+ MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+ return MSGPACK_UNPACK_PARSE_ERROR;
+ }
+@@ -681,7 +681,7 @@ int msgpack_unserialize_map_item(msgpack_unpack_data *unpack, zval **container,
+
+ case MSGPACK_SERIALIZE_TYPE_ENUM:
+ {
+- if (Z_TYPE_P(key) != IS_STRING) {
++ if (Z_TYPE_P(key) != IS_STRING || Z_TYPE_P(val) != IS_STRING) {
+ MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
+ return MSGPACK_UNPACK_PARSE_ERROR;
+ }
+@@ -736,7 +736,12 @@ int msgpack_unserialize_map_item(msgpack_unpack_data *unpack, zval **container,
+ case MSGPACK_SERIALIZE_TYPE_OBJECT:
+ case MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE:
+ {
+- zval *rval;
++ zval *rval, tmp;
++
++ if (Z_TYPE_P(val) != IS_LONG) {
++ MSGPACK_UNSERIALIZE_FINISH_MAP_ITEM(unpack, key, val);
++ return MSGPACK_UNPACK_PARSE_ERROR;
++ }
+
+ if (!(rval = msgpack_var_access(&unpack->var_hash, Z_LVAL_P(val) - 1))) {
+ if (UNEXPECTED(Z_LVAL_P(val) == 1 /* access the retval */)) {
+@@ -750,11 +755,13 @@ int msgpack_unserialize_map_item(msgpack_unpack_data *unpack, zval **container,
+ }
+ }
+
++ /* Retain the target before releasing the container: a crafted
++ * back-reference can point at the container itself. */
++ ZVAL_COPY(&tmp, rval);
+ if (container != NULL) {
+ zval_ptr_dtor(*container);
+ }
+-
+- ZVAL_COPY(*container, rval);
++ ZVAL_COPY_VALUE(*container, &tmp);
+ if (type == MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE) {
+ ZVAL_MAKE_REF(*container);
+ }
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/php-pecl-msgpack.git/commitdiff/d7f2a7ebf78042750206ef5e7bbc02a4df7c6333
More information about the pld-cvs-commit
mailing list