diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-11-11 11:51:20 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-11-11 11:56:03 +0100 |
commit | b4a2a9662b4244837e348f58d398523361dbcc7f (patch) | |
tree | 8a8f6b15216c330efb2286a7d941182225cce745 | |
parent | 11c4821ba968d2a39310851ab89d673c553b72f3 (diff) | |
download | php-git-b4a2a9662b4244837e348f58d398523361dbcc7f.tar.gz |
Retain reference to share handle from curl handle
Not keeping a reference will not result in use after free, because
curl protects against it, but it will result in a memory leak,
because curl_share_cleanup() will fail. We should make sure that
the share handle object stays alive as long as the curl handles
use it.
-rw-r--r-- | ext/curl/curl_private.h | 4 | ||||
-rw-r--r-- | ext/curl/interface.c | 10 | ||||
-rw-r--r-- | ext/curl/tests/curl_share_basic.phpt | 29 |
3 files changed, 42 insertions, 1 deletions
diff --git a/ext/curl/curl_private.h b/ext/curl/curl_private.h index 8ff0aef605..6ed2eba3c3 100644 --- a/ext/curl/curl_private.h +++ b/ext/curl/curl_private.h @@ -104,6 +104,8 @@ typedef struct { zend_bool in_callback; uint32_t* clone; zval postfields; + /* CurlShareHandle object set using CURLOPT_SHARE. */ + struct _php_curlsh *share; zend_object std; } php_curl; @@ -124,7 +126,7 @@ typedef struct { zend_object std; } php_curlm; -typedef struct { +typedef struct _php_curlsh { CURLSH *share; struct { int no; diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 8ff3e0f374..f01f6f775a 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2832,6 +2832,12 @@ static int _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue, bool i if (Z_TYPE_P(zvalue) == IS_OBJECT && Z_OBJCE_P(zvalue) == curl_share_ce) { php_curlsh *sh = Z_CURL_SHARE_P(zvalue); curl_easy_setopt(ch->cp, CURLOPT_SHARE, sh->share); + + if (ch->share) { + OBJ_RELEASE(&ch->share->std); + } + GC_ADDREF(&sh->std); + ch->share = sh; } } break; @@ -3373,6 +3379,10 @@ static void curl_free_obj(zend_object *object) efree(ch->handlers); zval_ptr_dtor(&ch->postfields); + if (ch->share) { + OBJ_RELEASE(&ch->share->std); + } + zend_object_std_dtor(&ch->std); } /* }}} */ diff --git a/ext/curl/tests/curl_share_basic.phpt b/ext/curl/tests/curl_share_basic.phpt new file mode 100644 index 0000000000..2a37fce16a --- /dev/null +++ b/ext/curl/tests/curl_share_basic.phpt @@ -0,0 +1,29 @@ +--TEST-- +Basic curl_share test +--FILE-- +<?php + +$sh = curl_share_init(); + +$ch1 = curl_init(); +curl_setopt($ch1, CURLOPT_URL, 'file://' . __DIR__ . '/curl_testdata1.txt'); +curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch1, CURLOPT_SHARE, $sh); + +$ch2 = curl_init(); +curl_setopt($ch2, CURLOPT_URL, 'file://' . __DIR__ . '/curl_testdata2.txt'); +curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch2, CURLOPT_SHARE, $sh); + +// Make sure nothing bad handles if the share handle is unset early. +unset($sh); + +var_dump(curl_exec($ch1)); +var_dump(curl_exec($ch2)); + +?> +--EXPECT-- +string(6) "CURL1 +" +string(6) "CURL2 +" |