summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS7
-rw-r--r--Zend/zend_execute.c2
-rw-r--r--Zend/zend_types.h61
-rw-r--r--Zend/zend_vm_def.h3
-rw-r--r--Zend/zend_vm_execute.h24
-rw-r--r--ext/standard/tests/serialize/bug68545.phpt11
-rw-r--r--ext/standard/var_unserializer.c75
-rw-r--r--ext/standard/var_unserializer.re8
-rw-r--r--sapi/fpm/config.m414
-rw-r--r--sapi/fpm/fpm/fpm_conf.c8
-rw-r--r--sapi/fpm/fpm/fpm_conf.h6
-rw-r--r--sapi/fpm/fpm/fpm_unix.c174
-rw-r--r--sapi/fpm/fpm/fpm_unix.h2
-rw-r--r--sapi/fpm/fpm/fpm_worker_pool.c2
-rw-r--r--sapi/fpm/fpm/fpm_worker_pool.h4
-rw-r--r--sapi/fpm/php-fpm.conf.in5
-rw-r--r--sapi/fpm/tests/021-uds-acl.phpt89
17 files changed, 434 insertions, 61 deletions
diff --git a/NEWS b/NEWS
index d2e68d0b6c..07bbd045fd 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,10 @@ PHP NEWS
. Fixed bug #68185 ("Inconsistent insteadof definition."- incorrectly triggered). (Julien)
. Fixed bug #65419 (Inside trait, self::class != __CLASS__). (Julien)
+- Date:
+ . Fixed day_of_week function as it could sometimes return negative values
+ internally. (Derick)
+
- DBA:
. Fixed bug #62490 (dba_delete returns true on missing item (inifile)). (Mike)
@@ -44,6 +48,9 @@ PHP NEWS
- LiteSpeed:
. Updated LiteSpeed SAPI code from V5.5 to V6.6. (George Wang)
+- Mcrypt:
+ . Fixed possible read after end of buffer and use after free. (Dmitry)
+
- pcntl:
. Fixed bug #60509 (pcntl_signal doesn't decrease ref-count of old handler
when setting SIG_DFL). (Julien)
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 283de41f7b..fbe52fe95a 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -934,7 +934,7 @@ static zend_always_inline void zend_assign_to_object_dim(zval *retval, zval *obj
}
}
-static void zend_binary_assign_op_obj_dim(zval *object, zval *property, zval *value, zval *retval, int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC))
+static void zend_binary_assign_op_obj_dim(zval *object, zval *property, zval *value, zval *retval, int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) TSRMLS_DC)
{
zval *z;
zval rv;
diff --git a/Zend/zend_types.h b/Zend/zend_types.h
index 77c914b209..c7591d6414 100644
--- a/Zend/zend_types.h
+++ b/Zend/zend_types.h
@@ -102,6 +102,11 @@ typedef union _zend_value {
void *ptr;
zend_class_entry *ce;
zend_function *func;
+ struct {
+ ZEND_ENDIAN_LOHI(
+ uint32_t w1,
+ uint32_t w2)
+ } ww;
} zend_value;
struct _zval_struct {
@@ -695,30 +700,64 @@ static zend_always_inline uint32_t zval_delref_p(zval* pz) {
return --GC_REFCOUNT(Z_COUNTED_P(pz));
}
+#if SIZEOF_ZEND_LONG == 4
+# define ZVAL_COPY_VALUE_EX(z, v, gc, t) \
+ do { \
+ uint32_t _w2; \
+ gc = v->value.counted; \
+ _w2 = v->value.ww.w2; \
+ t = Z_TYPE_INFO_P(v); \
+ z->value.counted = gc; \
+ z->value.ww.w2 = _w2; \
+ Z_TYPE_INFO_P(z) = t; \
+ } while (0)
+#elif SIZEOF_ZEND_LONG == 8
+# define ZVAL_COPY_VALUE_EX(z, v, gc, t) \
+ do { \
+ gc = v->value.counted; \
+ t = Z_TYPE_INFO_P(v); \
+ z->value.counted = gc; \
+ Z_TYPE_INFO_P(z) = t; \
+ } while (0)
+#else
+# error "Unknbown SIZEOF_ZEND_LONG"
+#endif
+
#define ZVAL_COPY_VALUE(z, v) \
do { \
zval *_z1 = (z); \
const zval *_z2 = (v); \
- (_z1)->value = (_z2)->value; \
- Z_TYPE_INFO_P(_z1) = Z_TYPE_INFO_P(_z2); \
+ zend_refcounted *_gc; \
+ uint32_t _t; \
+ ZVAL_COPY_VALUE_EX(_z1, _z2, _gc, _t); \
} while (0)
#define ZVAL_COPY(z, v) \
do { \
- zval *__z1 = (z); \
- const zval *__z2 = (v); \
- ZVAL_COPY_VALUE(__z1, __z2); \
- if (Z_OPT_REFCOUNTED_P(__z1)) { \
- Z_ADDREF_P(__z1); \
+ zval *_z1 = (z); \
+ const zval *_z2 = (v); \
+ zend_refcounted *_gc; \
+ uint32_t _t; \
+ ZVAL_COPY_VALUE_EX(_z1, _z2, _gc, _t); \
+ if ((_t & (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) != 0) { \
+ GC_REFCOUNT(_gc)++; \
} \
} while (0)
#define ZVAL_DUP(z, v) \
do { \
- zval *__z1 = (z); \
- const zval *__z2 = (v); \
- ZVAL_COPY_VALUE(__z1, __z2); \
- zval_opt_copy_ctor(__z1); \
+ zval *_z1 = (z); \
+ const zval *_z2 = (v); \
+ zend_refcounted *_gc; \
+ uint32_t _t; \
+ ZVAL_COPY_VALUE_EX(_z1, _z2, _gc, _t); \
+ if ((_t & ((IS_TYPE_REFCOUNTED|IS_TYPE_IMMUTABLE) << Z_TYPE_FLAGS_SHIFT)) != 0) { \
+ if ((_t & ((IS_TYPE_COPYABLE|IS_TYPE_IMMUTABLE) << Z_TYPE_FLAGS_SHIFT)) != 0) { \
+ _zval_copy_ctor_func(_z1 ZEND_FILE_LINE_CC); \
+ } else { \
+ GC_REFCOUNT(_gc)++; \
+ } \
+ } \
} while (0)
#define ZVAL_DEREF(z) do { \
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index aa8ddd3520..e03c7379eb 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -1715,9 +1715,10 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
FREE_OP2();
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = GET_OP2_ZVAL_PTR(BP_VAR_R);
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
FREE_OP2();
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 51dd045b2e..7ec9522444 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -13029,9 +13029,10 @@ try_assign_dim_array:
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = opline->op2.zv;
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
@@ -14549,9 +14550,10 @@ try_assign_dim_array:
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = NULL;
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
@@ -15896,9 +15898,10 @@ try_assign_dim_array:
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var TSRMLS_CC);
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
@@ -17425,9 +17428,10 @@ try_assign_dim_array:
zval_ptr_dtor_nogc(free_op2);
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
zval_ptr_dtor_nogc(free_op2);
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
@@ -25624,9 +25628,10 @@ try_assign_dim_array:
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = opline->op2.zv;
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
@@ -27949,9 +27954,10 @@ try_assign_dim_array:
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = NULL;
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
@@ -29635,9 +29641,10 @@ try_assign_dim_array:
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op2.var TSRMLS_CC);
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
@@ -31676,9 +31683,10 @@ try_assign_dim_array:
zval_ptr_dtor_nogc(free_op2);
} else if (EXPECTED(Z_TYPE_P(object_ptr) == IS_STRING) &&
EXPECTED(Z_STRLEN_P(object_ptr) != 0)) {
+ zend_long offset;
dim = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
- zend_long offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
+ offset = zend_fetch_string_offset(object_ptr, dim, BP_VAR_W TSRMLS_CC);
zval_ptr_dtor_nogc(free_op2);
value = get_zval_ptr_deref((opline+1)->op1_type, (opline+1)->op1, execute_data, &free_op_data1, BP_VAR_R);
zend_assign_to_string_offset(object_ptr, offset, value, (UNEXPECTED(RETURN_VALUE_USED(opline)) ? EX_VAR(opline->result.var) : NULL) TSRMLS_CC);
diff --git a/ext/standard/tests/serialize/bug68545.phpt b/ext/standard/tests/serialize/bug68545.phpt
new file mode 100644
index 0000000000..e7250b37bc
--- /dev/null
+++ b/ext/standard/tests/serialize/bug68545.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Bug #68545 NULL pointer dereference in unserialize.c:var_push_dtor
+--FILE--
+<?php
+var_dump(unserialize('a:6:{a:6:{s:3:"322";s:3:"bar";s:3:"bar";s:3:"foo";a:6:{a:6:{s:3:"322";s:3:"bar";s:3:"bar";s:3:"foo";s:3:"bar";a:6:{a:6:{s:3:"322";s:3:"bar";s:3:"bar";s:3:"foo";a:6:{a:6:{s:3:"322";s:3:"bar";s:3:"b22";s:3:"bar";s:3:"bar";s:3:"foo";s:3:"bar";a:6:{a:6:{s:3:"322";s:3:"bar";s:3:"bar";s:3:"foo";s:3:"bar";s:3:"bar";'));
+?>
+===DONE===
+--EXPECTF--
+Notice: unserialize(): Error at offset %d of %d bytes in %sbug68545.php on line %d
+bool(false)
+===DONE===
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index d8b94e75cc..0217bba5fa 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.13.7.5 */
+/* Generated by re2c 0.13.5 */
#line 1 "ext/standard/var_unserializer.re"
/*
+----------------------------------------------------------------------+
@@ -66,7 +66,13 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval *rval)
PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
{
- var_dtor_entries *var_hash = (*var_hashx)->last_dtor;
+ var_dtor_entries *var_hash;
+
+ if (!var_hashx || !*var_hashx) {
+ return;
+ }
+
+ var_hash = (*var_hashx)->last_dtor;
#if VAR_ENTRIES_DBG
fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
#endif
@@ -253,7 +259,7 @@ static inline int unserialize_allowed_class(zend_string *class_name, HashTable *
#define YYMARKER marker
-#line 261 "ext/standard/var_unserializer.re"
+#line 267 "ext/standard/var_unserializer.re"
@@ -509,7 +515,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER)
start = cursor;
-#line 513 "ext/standard/var_unserializer.c"
+#line 519 "ext/standard/var_unserializer.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -569,9 +575,9 @@ yy2:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy95;
yy3:
-#line 860 "ext/standard/var_unserializer.re"
+#line 866 "ext/standard/var_unserializer.re"
{ return 0; }
-#line 575 "ext/standard/var_unserializer.c"
+#line 581 "ext/standard/var_unserializer.c"
yy4:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy89;
@@ -614,13 +620,13 @@ yy13:
goto yy3;
yy14:
++YYCURSOR;
-#line 854 "ext/standard/var_unserializer.re"
+#line 860 "ext/standard/var_unserializer.re"
{
/* this is the case where we have less data than planned */
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data");
return 0; /* not sure if it should be 0 or 1 here? */
}
-#line 624 "ext/standard/var_unserializer.c"
+#line 630 "ext/standard/var_unserializer.c"
yy16:
yych = *++YYCURSOR;
goto yy3;
@@ -646,12 +652,11 @@ yy20:
if (yybm[0+yych] & 128) {
goto yy20;
}
- if (yych <= '/') goto yy18;
- if (yych >= ';') goto yy18;
+ if (yych != ':') goto yy18;
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 709 "ext/standard/var_unserializer.re"
+#line 715 "ext/standard/var_unserializer.re"
{
size_t len, len2, len3, maxlen;
zend_long elements;
@@ -796,7 +801,7 @@ yy20:
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 800 "ext/standard/var_unserializer.c"
+#line 805 "ext/standard/var_unserializer.c"
yy25:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -821,7 +826,7 @@ yy27:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 701 "ext/standard/var_unserializer.re"
+#line 707 "ext/standard/var_unserializer.re"
{
//??? INIT_PZVAL(rval);
@@ -829,7 +834,7 @@ yy27:
return object_common2(UNSERIALIZE_PASSTHRU,
object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
}
-#line 833 "ext/standard/var_unserializer.c"
+#line 838 "ext/standard/var_unserializer.c"
yy32:
yych = *++YYCURSOR;
if (yych == '+') goto yy33;
@@ -850,7 +855,7 @@ yy34:
yych = *++YYCURSOR;
if (yych != '{') goto yy18;
++YYCURSOR;
-#line 680 "ext/standard/var_unserializer.re"
+#line 686 "ext/standard/var_unserializer.re"
{
zend_long elements = parse_iv(start + 2);
/* use iv() not uiv() in order to check data range */
@@ -871,7 +876,7 @@ yy34:
return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
-#line 875 "ext/standard/var_unserializer.c"
+#line 880 "ext/standard/var_unserializer.c"
yy39:
yych = *++YYCURSOR;
if (yych == '+') goto yy40;
@@ -892,7 +897,7 @@ yy41:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 652 "ext/standard/var_unserializer.re"
+#line 658 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
zend_string *str;
@@ -920,7 +925,7 @@ yy41:
ZVAL_STR(rval, str);
return 1;
}
-#line 924 "ext/standard/var_unserializer.c"
+#line 929 "ext/standard/var_unserializer.c"
yy46:
yych = *++YYCURSOR;
if (yych == '+') goto yy47;
@@ -941,7 +946,7 @@ yy48:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 625 "ext/standard/var_unserializer.re"
+#line 631 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -968,7 +973,7 @@ yy48:
ZVAL_STRINGL(rval, str, len);
return 1;
}
-#line 972 "ext/standard/var_unserializer.c"
+#line 977 "ext/standard/var_unserializer.c"
yy53:
yych = *++YYCURSOR;
if (yych <= '/') {
@@ -1056,7 +1061,7 @@ yy61:
}
yy63:
++YYCURSOR;
-#line 616 "ext/standard/var_unserializer.re"
+#line 622 "ext/standard/var_unserializer.re"
{
#if SIZEOF_ZEND_LONG == 4
use_double:
@@ -1065,7 +1070,7 @@ use_double:
ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL));
return 1;
}
-#line 1069 "ext/standard/var_unserializer.c"
+#line 1074 "ext/standard/var_unserializer.c"
yy65:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1124,7 +1129,7 @@ yy73:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 600 "ext/standard/var_unserializer.re"
+#line 606 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
@@ -1140,7 +1145,7 @@ yy73:
return 1;
}
-#line 1144 "ext/standard/var_unserializer.c"
+#line 1149 "ext/standard/var_unserializer.c"
yy76:
yych = *++YYCURSOR;
if (yych == 'N') goto yy73;
@@ -1167,7 +1172,7 @@ yy79:
if (yych <= '9') goto yy79;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 574 "ext/standard/var_unserializer.re"
+#line 580 "ext/standard/var_unserializer.re"
{
#if SIZEOF_ZEND_LONG == 4
int digits = YYCURSOR - start - 3;
@@ -1193,7 +1198,7 @@ yy79:
ZVAL_LONG(rval, parse_iv(start + 2));
return 1;
}
-#line 1197 "ext/standard/var_unserializer.c"
+#line 1202 "ext/standard/var_unserializer.c"
yy83:
yych = *++YYCURSOR;
if (yych <= '/') goto yy18;
@@ -1201,22 +1206,22 @@ yy83:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 568 "ext/standard/var_unserializer.re"
+#line 574 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
ZVAL_BOOL(rval, parse_iv(start + 2));
return 1;
}
-#line 1211 "ext/standard/var_unserializer.c"
+#line 1216 "ext/standard/var_unserializer.c"
yy87:
++YYCURSOR;
-#line 562 "ext/standard/var_unserializer.re"
+#line 568 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
ZVAL_NULL(rval);
return 1;
}
-#line 1220 "ext/standard/var_unserializer.c"
+#line 1225 "ext/standard/var_unserializer.c"
yy89:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1239,7 +1244,7 @@ yy91:
if (yych <= '9') goto yy91;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 539 "ext/standard/var_unserializer.re"
+#line 545 "ext/standard/var_unserializer.re"
{
zend_long id;
@@ -1262,7 +1267,7 @@ yy91:
return 1;
}
-#line 1266 "ext/standard/var_unserializer.c"
+#line 1271 "ext/standard/var_unserializer.c"
yy95:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1285,7 +1290,7 @@ yy97:
if (yych <= '9') goto yy97;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 517 "ext/standard/var_unserializer.re"
+#line 523 "ext/standard/var_unserializer.re"
{
zend_long id;
@@ -1307,9 +1312,9 @@ yy97:
return 1;
}
-#line 1311 "ext/standard/var_unserializer.c"
+#line 1316 "ext/standard/var_unserializer.c"
}
-#line 862 "ext/standard/var_unserializer.re"
+#line 868 "ext/standard/var_unserializer.re"
return 0;
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 7e0dc314d6..c7871671b6 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -64,7 +64,13 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval *rval)
PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval *rval)
{
- var_dtor_entries *var_hash = (*var_hashx)->last_dtor;
+ var_dtor_entries *var_hash;
+
+ if (!var_hashx || !*var_hashx) {
+ return;
+ }
+
+ var_hash = (*var_hashx)->last_dtor;
#if VAR_ENTRIES_DBG
fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
#endif
diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4
index 9c10aa6be2..f87776aa24 100644
--- a/sapi/fpm/config.m4
+++ b/sapi/fpm/config.m4
@@ -583,6 +583,9 @@ if test "$PHP_FPM" != "no"; then
PHP_ARG_WITH(fpm-systemd,,
[ --with-fpm-systemd Activate systemd integration], no, no)
+ PHP_ARG_WITH(fpm-acl,,
+ [ --with-fpm-acl Use POSIX Access Control Lists], no, no)
+
if test "$PHP_FPM_SYSTEMD" != "no" ; then
if test -z "$PKG_CONFIG"; then
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
@@ -624,6 +627,17 @@ if test "$PHP_FPM" != "no"; then
else
php_fpm_systemd=simple
fi
+
+ if test "$PHP_FPM_ACL" != "no" ; then
+ AC_CHECK_HEADERS([sys/acl.h])
+ AC_CHECK_LIB(acl, acl_free, [
+ PHP_ADD_LIBRARY(acl)
+ AC_DEFINE(HAVE_FPM_ACL, 1, [ POSIX Access Control List ])
+ ],[
+ AC_MSG_ERROR(libacl required not found)
+ ])
+ fi
+
PHP_SUBST_OLD(php_fpm_systemd)
AC_DEFINE_UNQUOTED(PHP_FPM_SYSTEMD, "$php_fpm_systemd", [fpm systemd service type])
diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c
index d4e46853ff..80bc27c7a7 100644
--- a/sapi/fpm/fpm/fpm_conf.c
+++ b/sapi/fpm/fpm/fpm_conf.c
@@ -123,6 +123,10 @@ static struct ini_value_parser_s ini_fpm_pool_options[] = {
{ "group", &fpm_conf_set_string, WPO(group) },
{ "listen", &fpm_conf_set_string, WPO(listen_address) },
{ "listen.backlog", &fpm_conf_set_integer, WPO(listen_backlog) },
+#ifdef HAVE_FPM_ACL
+ { "listen.acl_users", &fpm_conf_set_string, WPO(listen_acl_users) },
+ { "listen.acl_groups", &fpm_conf_set_string, WPO(listen_acl_groups) },
+#endif
{ "listen.owner", &fpm_conf_set_string, WPO(listen_owner) },
{ "listen.group", &fpm_conf_set_string, WPO(listen_group) },
{ "listen.mode", &fpm_conf_set_string, WPO(listen_mode) },
@@ -1602,6 +1606,10 @@ static void fpm_conf_dump() /* {{{ */
zlog(ZLOG_NOTICE, "\tgroup = %s", STR2STR(wp->config->group));
zlog(ZLOG_NOTICE, "\tlisten = %s", STR2STR(wp->config->listen_address));
zlog(ZLOG_NOTICE, "\tlisten.backlog = %d", wp->config->listen_backlog);
+#ifdef HAVE_FPM_ACL
+ zlog(ZLOG_NOTICE, "\tlisten.acl_users = %s", STR2STR(wp->config->listen_acl_users));
+ zlog(ZLOG_NOTICE, "\tlisten.acl_groups = %s", STR2STR(wp->config->listen_acl_groups));
+#endif
zlog(ZLOG_NOTICE, "\tlisten.owner = %s", STR2STR(wp->config->listen_owner));
zlog(ZLOG_NOTICE, "\tlisten.group = %s", STR2STR(wp->config->listen_group));
zlog(ZLOG_NOTICE, "\tlisten.mode = %s", STR2STR(wp->config->listen_mode));
diff --git a/sapi/fpm/fpm/fpm_conf.h b/sapi/fpm/fpm/fpm_conf.h
index 12fabe2805..540b22795d 100644
--- a/sapi/fpm/fpm/fpm_conf.h
+++ b/sapi/fpm/fpm/fpm_conf.h
@@ -58,6 +58,7 @@ struct fpm_worker_pool_config_s {
char *group;
char *listen_address;
int listen_backlog;
+ /* Using chown */
char *listen_owner;
char *listen_group;
char *listen_mode;
@@ -91,6 +92,11 @@ struct fpm_worker_pool_config_s {
#ifdef HAVE_APPARMOR
char *apparmor_hat;
#endif
+#ifdef HAVE_FPM_ACL
+ /* Using Posix ACL */
+ char *listen_acl_users;
+ char *listen_acl_groups;
+#endif
};
struct ini_value_parser_s {
diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c
index 57707d8f8a..f0d4573483 100644
--- a/sapi/fpm/fpm/fpm_unix.c
+++ b/sapi/fpm/fpm/fpm_unix.c
@@ -21,6 +21,10 @@
#include <sys/apparmor.h>
#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+
#include "fpm.h"
#include "fpm_conf.h"
#include "fpm_cleanup.h"
@@ -35,8 +39,12 @@ size_t fpm_pagesize;
int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
{
struct fpm_worker_pool_config_s *c = wp->config;
+#ifdef HAVE_FPM_ACL
+ int n;
/* uninitialized */
+ wp->socket_acl = NULL;
+#endif
wp->socket_uid = -1;
wp->socket_gid = -1;
wp->socket_mode = 0660;
@@ -45,6 +53,117 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
return 0;
}
+ if (c->listen_mode && *c->listen_mode) {
+ wp->socket_mode = strtoul(c->listen_mode, 0, 8);
+ }
+
+#ifdef HAVE_FPM_ACL
+ /* count the users and groups configured */
+ n = 0;
+ if (c->listen_acl_users && *c->listen_acl_users) {
+ char *p;
+ n++;
+ for (p=strchr(c->listen_acl_users, ',') ; p ; p=strchr(p+1, ',')) {
+ n++;
+ }
+ }
+ if (c->listen_acl_groups && *c->listen_acl_groups) {
+ char *p;
+ n++;
+ for (p=strchr(c->listen_acl_groups, ',') ; p ; p=strchr(p+1, ',')) {
+ n++;
+ }
+ }
+ /* if ACL configured */
+ if (n) {
+ acl_t acl;
+ acl_entry_t entry;
+ acl_permset_t perm;
+ char *tmp, *p, *end;
+
+ acl = acl_init(n);
+ if (!acl) {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot allocate ACL", wp->config->name);
+ return -1;
+ }
+ /* Create USER ACL */
+ if (c->listen_acl_users && *c->listen_acl_users) {
+ struct passwd *pwd;
+
+ tmp = estrdup(c->listen_acl_users);
+ for (p=tmp ; p ; p=end) {
+ if ((end = strchr(p, ','))) {
+ *end++ = 0;
+ }
+ pwd = getpwnam(p);
+ if (pwd) {
+ zlog(ZLOG_DEBUG, "[pool %s] user '%s' have uid=%d", wp->config->name, p, pwd->pw_uid);
+ } else {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, p);
+ acl_free(acl);
+ efree(tmp);
+ return -1;
+ }
+ if (0 > acl_create_entry(&acl, &entry) ||
+ 0 > acl_set_tag_type(entry, ACL_USER) ||
+ 0 > acl_set_qualifier(entry, &pwd->pw_uid) ||
+ 0 > acl_get_permset(entry, &perm) ||
+ 0 > acl_clear_perms (perm) ||
+ 0 > acl_add_perm (perm, ACL_READ) ||
+ 0 > acl_add_perm (perm, ACL_WRITE)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot create ACL for user '%s'", wp->config->name, p);
+ acl_free(acl);
+ efree(tmp);
+ return -1;
+ }
+ }
+ efree(tmp);
+ }
+ /* Create GROUP ACL */
+ if (c->listen_acl_groups && *c->listen_acl_groups) {
+ struct group *grp;
+
+ tmp = estrdup(c->listen_acl_groups);
+ for (p=tmp ; p ; p=end) {
+ if ((end = strchr(p, ','))) {
+ *end++ = 0;
+ }
+ grp = getgrnam(p);
+ if (grp) {
+ zlog(ZLOG_DEBUG, "[pool %s] group '%s' have gid=%d", wp->config->name, p, grp->gr_gid);
+ } else {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, p);
+ acl_free(acl);
+ efree(tmp);
+ return -1;
+ }
+ if (0 > acl_create_entry(&acl, &entry) ||
+ 0 > acl_set_tag_type(entry, ACL_GROUP) ||
+ 0 > acl_set_qualifier(entry, &grp->gr_gid) ||
+ 0 > acl_get_permset(entry, &perm) ||
+ 0 > acl_clear_perms (perm) ||
+ 0 > acl_add_perm (perm, ACL_READ) ||
+ 0 > acl_add_perm (perm, ACL_WRITE)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot create ACL for group '%s'", wp->config->name, p);
+ acl_free(acl);
+ efree(tmp);
+ return -1;
+ }
+ }
+ efree(tmp);
+ }
+ if (c->listen_owner && *c->listen_owner) {
+ zlog(ZLOG_WARNING, "[pool %s] ACL set, listen.owner = '%s' is ignored", wp->config->name, c->listen_owner);
+ }
+ if (c->listen_group && *c->listen_group) {
+ zlog(ZLOG_WARNING, "[pool %s] ACL set, listen.group = '%s' is ignored", wp->config->name, c->listen_group);
+ }
+ wp->socket_acl = acl;
+ return 0;
+ }
+ /* When listen.users and listen.groups not configured, continue with standard right */
+#endif
+
if (c->listen_owner && *c->listen_owner) {
struct passwd *pwd;
@@ -69,18 +188,54 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
wp->socket_gid = grp->gr_gid;
}
- if (c->listen_mode && *c->listen_mode) {
- wp->socket_mode = strtoul(c->listen_mode, 0, 8);
- }
return 0;
}
/* }}} */
int fpm_unix_set_socket_premissions(struct fpm_worker_pool_s *wp, const char *path) /* {{{ */
{
+#ifdef HAVE_FPM_ACL
+ if (wp->socket_acl) {
+ acl_t aclfile, aclconf;
+ acl_entry_t entryfile, entryconf;
+ int i;
+
+ /* Read the socket ACL */
+ aclconf = wp->socket_acl;
+ aclfile = acl_get_file (path, ACL_TYPE_ACCESS);
+ if (!aclfile) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to read the ACL of the socket '%s'", wp->config->name, path);
+ return -1;
+ }
+ /* Copy the new ACL entry from config */
+ for (i=ACL_FIRST_ENTRY ; acl_get_entry(aclconf, i, &entryconf) ; i=ACL_NEXT_ENTRY) {
+ if (0 > acl_create_entry (&aclfile, &entryfile) ||
+ 0 > acl_copy_entry(entryfile, entryconf)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to add entry to the ACL of the socket '%s'", wp->config->name, path);
+ acl_free(aclfile);
+ return -1;
+ }
+ }
+ /* Write the socket ACL */
+ if (0 > acl_calc_mask (&aclfile) ||
+ 0 > acl_valid (aclfile) ||
+ 0 > acl_set_file (path, ACL_TYPE_ACCESS, aclfile)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to write the ACL of the socket '%s'", wp->config->name, path);
+ acl_free(aclfile);
+ return -1;
+ } else {
+ zlog(ZLOG_DEBUG, "[pool %s] ACL of the socket '%s' is set", wp->config->name, path);
+ }
+
+ acl_free(aclfile);
+ return 0;
+ }
+ /* When listen.users and listen.groups not configured, continue with standard right */
+#endif
+
if (wp->socket_uid != -1 || wp->socket_gid != -1) {
if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
- zlog(ZLOG_SYSERROR, "failed to chown() the socket '%s'", wp->config->listen_address);
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to chown() the socket '%s'", wp->config->name, wp->config->listen_address);
return -1;
}
}
@@ -88,6 +243,17 @@ int fpm_unix_set_socket_premissions(struct fpm_worker_pool_s *wp, const char *pa
}
/* }}} */
+int fpm_unix_free_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+#ifdef HAVE_FPM_ACL
+ if (wp->socket_acl) {
+ return acl_free(wp->socket_acl);
+ }
+#endif
+ return 0;
+}
+/* }}} */
+
static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
{
struct passwd *pwd;
diff --git a/sapi/fpm/fpm/fpm_unix.h b/sapi/fpm/fpm/fpm_unix.h
index b2995ff3e0..a79559f9e6 100644
--- a/sapi/fpm/fpm/fpm_unix.h
+++ b/sapi/fpm/fpm/fpm_unix.h
@@ -9,6 +9,8 @@
int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp);
int fpm_unix_set_socket_premissions(struct fpm_worker_pool_s *wp, const char *path);
+int fpm_unix_free_socket_premissions(struct fpm_worker_pool_s *wp);
+
int fpm_unix_init_child(struct fpm_worker_pool_s *wp);
int fpm_unix_init_main();
diff --git a/sapi/fpm/fpm/fpm_worker_pool.c b/sapi/fpm/fpm/fpm_worker_pool.c
index ebe1866c8a..a0022915cd 100644
--- a/sapi/fpm/fpm/fpm_worker_pool.c
+++ b/sapi/fpm/fpm/fpm_worker_pool.c
@@ -15,6 +15,7 @@
#include "fpm_shm.h"
#include "fpm_scoreboard.h"
#include "fpm_conf.h"
+#include "fpm_unix.h"
struct fpm_worker_pool_s *fpm_worker_all_pools;
@@ -29,6 +30,7 @@ void fpm_worker_pool_free(struct fpm_worker_pool_s *wp) /* {{{ */
if (wp->home) {
free(wp->home);
}
+ fpm_unix_free_socket_premissions(wp);
free(wp);
}
/* }}} */
diff --git a/sapi/fpm/fpm/fpm_worker_pool.h b/sapi/fpm/fpm/fpm_worker_pool.h
index 05c993de4e..6b2bc908dc 100644
--- a/sapi/fpm/fpm/fpm_worker_pool.h
+++ b/sapi/fpm/fpm/fpm_worker_pool.h
@@ -42,6 +42,10 @@ struct fpm_worker_pool_s {
/* for ondemand PM */
struct fpm_event_s *ondemand_event;
int socket_event_set;
+
+#ifdef HAVE_FPM_ACL
+ void *socket_acl;
+#endif
};
struct fpm_worker_pool_s *fpm_worker_pool_alloc();
diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in
index 850a369002..6f384fb131 100644
--- a/sapi/fpm/php-fpm.conf.in
+++ b/sapi/fpm/php-fpm.conf.in
@@ -173,6 +173,11 @@ listen = 127.0.0.1:9000
;listen.owner = @php_fpm_user@
;listen.group = @php_fpm_group@
;listen.mode = 0660
+; When POSIX Access Control Lists are supported you can set them using
+; these options, value is a coma separated list of user/group names.
+; When set, listen.owner and listen.group are ignored
+;listen.acl_users =
+;listen.acl_groups =
; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
diff --git a/sapi/fpm/tests/021-uds-acl.phpt b/sapi/fpm/tests/021-uds-acl.phpt
new file mode 100644
index 0000000000..f39c526418
--- /dev/null
+++ b/sapi/fpm/tests/021-uds-acl.phpt
@@ -0,0 +1,89 @@
+--TEST--
+FPM: Test Unix Domain Socket with Posix ACL
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!(file_exists('/usr/bin/getfacl') && file_exists('/etc/passwd') && file_exists('/etc/group'))) die ("skip missing getfacl command");
+?>
+--XFAIL--
+Mark as XFAIL because --with-fpm-acl is not enabled in default build
+--FILE--
+<?php
+
+include "include.inc";
+
+$logfile = dirname(__FILE__).'/php-fpm.log.tmp';
+$socket = dirname(__FILE__).'/php-fpm.sock';
+
+// Select 3 users and 2 groups known by system (avoid root)
+$users = $groups = [];
+$tmp = file('/etc/passwd');
+for ($i=1 ; $i<=3 ; $i++) {
+ $tab = explode(':', $tmp[$i]);
+ $users[] = $tab[0];
+}
+$users = implode(',', $users);
+$tmp = file('/etc/group');
+for ($i=1 ; $i<=2 ; $i++) {
+ $tab = explode(':', $tmp[$i]);
+ $groups[] = $tab[0];
+}
+$groups = implode(',', $groups);
+
+$cfg = <<<EOT
+[global]
+error_log = $logfile
+[unconfined]
+listen = $socket
+listen.acl_users = $users
+listen.acl_groups = $groups
+listen.mode = 0600
+ping.path = /ping
+ping.response = pong
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 2
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+EOT;
+
+$fpm = run_fpm($cfg, $tail);
+if (is_resource($fpm)) {
+ fpm_display_log($tail, 2);
+ try {
+ var_dump(strpos(run_request('unix://'.$socket, -1), 'pong'));
+ echo "UDS ok\n";
+ } catch (Exception $e) {
+ echo "UDS error\n";
+ }
+ passthru("/usr/bin/getfacl -cp $socket");
+
+ proc_terminate($fpm);
+ echo stream_get_contents($tail);
+ fclose($tail);
+ proc_close($fpm);
+}
+
+?>
+--EXPECTF--
+[%s] NOTICE: fpm is running, pid %d
+[%s] NOTICE: ready to handle connections
+int(%d)
+UDS ok
+user::rw-
+user:%s:rw-
+user:%s:rw-
+user:%s:rw-
+group::---
+group:%s:rw-
+group:%s:rw-
+mask::rw-
+other::---
+
+[%s] NOTICE: Terminating ...
+[%s] NOTICE: exiting, bye-bye!
+--CLEAN--
+<?php
+ $logfile = dirname(__FILE__).'/php-fpm.log.tmp';
+ @unlink($logfile);
+?>