Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ea3ea56

Browse files
Fix: Harden PhpRedis against protocol errors
* Fix a double free when zipping keys and scores. * Instead of aborting with an assertion if elements != 2 just warn and return failure * Instead of crashing on `xclaim` reply shape issues, just return false
1 parent f14ce60 commit ea3ea56

2 files changed

Lines changed: 42 additions & 14 deletions

File tree

library.c

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,14 +1573,21 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab,
15731573
continue; /* this should never happen, according to the PHP people. */
15741574
}
15751575

1576-
/* get current value, a hash value now. */
1577-
char *hval = Z_STRVAL_P(z_value_p);
1578-
15791576
/* Decode the score depending on flag */
1580-
if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) {
1581-
ZVAL_LONG(&z_sub, atoi(hval+1));
1582-
} else if (decode == SCORE_DECODE_DOUBLE) {
1583-
ZVAL_DOUBLE(&z_sub, atof(hval));
1577+
if (decode == SCORE_DECODE_INT || decode == SCORE_DECODE_DOUBLE) {
1578+
zend_string *aux, *val;
1579+
1580+
val = zval_get_tmp_string(z_value_p, &aux);
1581+
1582+
if (decode == SCORE_DECODE_INT && ZSTR_LEN(val) > 0) {
1583+
ZVAL_LONG(&z_sub, atoi(ZSTR_VAL(val)+1));
1584+
} else if (decode == SCORE_DECODE_DOUBLE) {
1585+
ZVAL_DOUBLE(&z_sub, atof(ZSTR_VAL(val)));
1586+
} else {
1587+
ZVAL_ZVAL(&z_sub, z_value_p, 1, 0);
1588+
}
1589+
1590+
zend_tmp_string_release(aux);
15841591
} else {
15851592
ZVAL_ZVAL(&z_sub, z_value_p, 1, 0);
15861593
}
@@ -1695,8 +1702,10 @@ redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements,
16951702
return SUCCESS;
16961703
}
16971704

1698-
/* Invariant: We should have two elements */
1699-
ZEND_ASSERT(elements == 2);
1705+
if (UNEXPECTED(elements != 2)) {
1706+
php_error_docref(NULL, E_WARNING, "Expected 2 elements in response, got %d", elements);
1707+
return FAILURE;
1708+
}
17001709

17011710
array_init_size(zdst, 2);
17021711

@@ -2066,6 +2075,8 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret) {
20662075

20672076
/* Iterate over each message */
20682077
for (i = 0; i < count; i++) {
2078+
id = NULL;
2079+
20692080
/* Consume inner multi-bulk header, message ID itself and finally
20702081
* the multi-bulk header for field and values */
20712082
if ((read_mbulk_header(redis_sock, &mhdr) < 0 || mhdr != 2) ||
@@ -2081,7 +2092,11 @@ redis_read_stream_messages(RedisSock *redis_sock, int count, zval *z_ret) {
20812092
add_assoc_null_ex(z_ret, id, idlen);
20822093
} else {
20832094
array_init_size(&z_message, fields > 0 ? fields : 0);
2084-
redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS);
2095+
if (redis_mbulk_reply_loop(redis_sock, &z_message, fields, UNSERIALIZE_VALS) < 0) {
2096+
zval_ptr_dtor_nogc(&z_message);
2097+
efree(id);
2098+
return -1;
2099+
}
20852100
array_zip_values_and_scores(redis_sock, &z_message, SCORE_DECODE_NONE);
20862101
add_assoc_zval_ex(z_ret, id, idlen, &z_message);
20872102
}
@@ -2213,7 +2228,11 @@ redis_read_xclaim_ids(RedisSock *redis_sock, int count, zval *rv) {
22132228

22142229
array_init_size(&z_msg, fields > 0 ? fields : 0);
22152230

2216-
redis_mbulk_reply_loop(redis_sock, &z_msg, fields, UNSERIALIZE_VALS);
2231+
if (redis_mbulk_reply_loop(redis_sock, &z_msg, fields, UNSERIALIZE_VALS) < 0) {
2232+
zval_ptr_dtor_nogc(&z_msg);
2233+
efree(id);
2234+
return -1;
2235+
}
22172236
array_zip_values_and_scores(redis_sock, &z_msg, SCORE_DECODE_NONE);
22182237
add_assoc_zval_ex(rv, id, idlen, &z_msg);
22192238
efree(id);
@@ -2234,7 +2253,9 @@ redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim,
22342253
long id_len = 0;
22352254
int messages = 0;
22362255

2237-
ZEND_ASSERT(!is_xautoclaim || (count == 2 || count == 3));
2256+
if (is_xautoclaim && (count != 2 && count != 3)) {
2257+
return -1;
2258+
}
22382259

22392260
ZVAL_UNDEF(rv);
22402261

@@ -3740,7 +3761,7 @@ redis_sock_read_zstr(RedisSock *redis_sock)
37403761
return NULL;
37413762
}
37423763

3743-
PHP_REDIS_API void
3764+
PHP_REDIS_API int
37443765
redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count,
37453766
int unserialize)
37463767
{
@@ -3757,6 +3778,11 @@ redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count,
37573778
for (i = 0; i < count; ++i) {
37583779
if ((zstr = redis_sock_read_zstr(redis_sock)) == NULL) {
37593780
add_next_index_bool(z_tab, 0);
3781+
if (EG(exception) || redis_sock->stream == NULL ||
3782+
redis_sock->status == REDIS_SOCK_STATUS_FAILED
3783+
) {
3784+
return FAILURE;
3785+
}
37603786
continue;
37613787
}
37623788

@@ -3777,6 +3803,8 @@ redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count,
37773803
}
37783804
zend_hash_next_index_insert_new(Z_ARRVAL_P(z_tab), &z_value);
37793805
}
3806+
3807+
return SUCCESS;
37803808
}
37813809

37823810
static int

library.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force, int is
103103
PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(RedisSock *redis_sock, zval *z_tab);
104104
PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes);
105105
PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, RedisCmdCtx ctx);
106-
PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize);
106+
PHP_REDIS_API int redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize);
107107

108108

109109
PHP_REDIS_API int redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, RedisCmdCtx ctx);

0 commit comments

Comments
 (0)