Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

PR for Password Hash RFC #191

Merged
merged 48 commits into from over 1 year ago

4 participants

Anthony Ferrara Nikita Popov Lars Strojny Account for PHP Pull Requests
Anthony Ferrara

This is the pull request for the Password Hash RFC which was accepted.

Please review the changes, and if all looks good I will merge the PR in.

Thanks

Anthony

and others added some commits June 24, 2012
Base structure for passsword_create and password_make_salt c77f2c2
Actually complete password_create() 7e41980
Implement password_verify 6574028
Fix memory leak on branch f7097d9
Basic random generator added to make_salt 18d3bd9
More error checking, and some cleaning up for password.c 618f262
Anthony Ferrara Implement openssl support for make_salt 41d7374
Refactor salt generation, rename password_create to password_hash 2d4b7cb
Implement php.ini setting password.bcrypt_cost 232da90
Add tests for password hashing e505316
Update tests to check ini setting 2b9591f
Add tests and error checking for large salt requested values to preve…
…nt overflow on allocation
5f44be0
Anthony Ferrara Fix formatting issues in password.c 0dd2f16
Anthony Ferrara Refactor crypt to use an external working function 6bb3865
Anthony Ferrara Refactor password.c a bit, add different error checking da3d8bf
Anthony Ferrara Merge remote branch 'upstream/master' into hash_password
Conflicts:
	ext/standard/crypt.c
9e18e57
Anthony Ferrara More refactoring of crypt into php_crypt, and fixing memory allocation 9c1445c
Anthony Ferrara Update password.c to use safe_emalloc in sensitive places f53112f
Remove php.ini setting for default bcrypt cost 6cc3c65
Some more refactoring, make algo no longer optional 6943f2a
Update signature info for changing algo to an ordinal 886527d
Anthony Ferrara Implement password_needs_rehash() function 5160dc1
Anthony Ferrara Fix issue with int vs long parameter db86d54
Anthony Ferrara Implement password_get_info() function ee7e799
Anthony Ferrara Cleanup whitespace issues 9d3630b
Anthony Ferrara Merge remote branch 'upstream/master' into hash_password
* upstream/master: (34 commits)
  Fixed Bug #62500 (Segfault in DateInterval class when extended)
  Fixed test bug #62312 (warnings changed one more time)
  fix valgrind warning
  fix valgrind warning
  fixed #62433 test for win
  update NEWS
  Fixed bug #62499 (curl_setopt($ch, CURLOPT_COOKIEFILE, "") returns false)
  appease MSVC (doesnt like unary minus of unsigned ints)
  appease MSVC (doesnt like unary minus of unsigned ints)
  appease MSVC (doesnt like unary minus of unsigned ints)
  - Fixed bug #62507 (['REQUEST_TIME'] under mod_php5 returns miliseconds instead of seconds)
  Fixed Bug #62500 (Segfault in DateInterval class when extended)
  Added in NEWS and UPGRADING for feature 55218
  Fix two issues with run-tests.php
  Fix potential integer overflow in nl2br
  Fix potential integer overflow in bin2hex
  This wil be PHP 5.3.16
  Revert change 3f3ad30: There shouldn't be new features in 5.3, especially not if they aren't in 5.4, too.
  fix (signed) integer overflow (part of bug #52550
  fix (signed) integer overflow (part of bug #52550
  ...
99b7956
Anthony Ferrara Switch second parameter to password_make_salt to be a flag 707c907
Anthony Ferrara Remove password_make_salt() from the implementation e05413c
Anthony Ferrara Merge remote branch 'upstream/master' into hash_password
* upstream/master: (393 commits)
  forked two tests for windows
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice)
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice).
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice).
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice)
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice)
  Bug #49510: Boolean validation fails with FILTER_NULL_ON_FAILURE with empty string or false
  Implemented ReflectionFunction::isGenerator()
  Allow null as a default value for length in mb_substr() and mb_strcut()
  Allow null as a default value for length in mb_substr() and mb_strcut()
  folder
  Initializing optional argument description in assert()
  Initializing optional argument description in assert()
  Fix test failed due to new Token T_YIELD
  fix NEWS
  Fix leak when yielding array as key
  Drop obsolete test
  Remove extra blank in notice message, should act as same as vm
  Fixed bug #62987 (Assigning to ArrayObject[null][something] overrides all undefined variables)
  assert() user message
  ...
824f1f4
Anthony Ferrara Refactoring to use size_t instead of int most places db41f9f
Anthony Ferrara Add tests for password_get_info and password_needs_rehash e8b7f5b
Anthony Ferrara Switch test to using strict comparison for crypt fallback e9a7bde
Anthony Ferrara Remove bcrypt_cost ini entry from declaration ebe0bd5
Anthony Ferrara Expose PASSWORD_BCRYPT_DEFAULT_COST constant and update test to use it 76f3295
Anthony Ferrara Merge remote branch 'upstream/master' into hash_password
* upstream/master:
  Generators & UPGRADING
  Capitalize the warning message
  Rearrange the codes, remove empty lines
  Provide a specific error message if date.timezone value is invalid.
  regenerate patch, and save a strlen
  Fix the wrong use of snprintf which is introduced in 1d2f619
  Fixed unintendent clearance of PHP_OUTPUT_ACTIVATED flag
  Fix doc bug #63032 (Number of release in documentation).
  fixed test for bug #52944 also for darwin
  -enable VC11 (vc2012)
  - Fixed bug #61767 (Shutdown functions not called in certain error situation) - Fixed bug #60909 (custom error handler throwing Exception + fatal error = no shutdown function)
  year++
  correct PHP version & year++
  Correct PHP version
  year++
  - fix build, there was no tsrm context there, doing a fetch but this is horribly slow, this fix needs improvement (or simply add a TSRM context in the signature in master
  - fix build, declarations must be 1st in a contextgit checkout -f master
  Fixed bug #62907 (Double free when use traits)
  Fixed bug #62991 (Segfault with generator and closure)
3e383dc
Anthony Ferrara Add news entry for password API 7161c3d
ext/standard/basic_functions.c
@@ -1854,6 +1854,25 @@
1854 1854
 ZEND_BEGIN_ARG_INFO(arginfo_getlastmod, 0)
1855 1855
 ZEND_END_ARG_INFO()
1856 1856
 /* }}} */
  1857
+/* {{{ password.c */
  1858
+ZEND_BEGIN_ARG_INFO_EX(arginfo_password_hash, 0, 0, 1)
1
Nikita Popov
nikic added a note September 12, 2012

Shouldn't the last arg be 2 as the algo is also a required argument?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Nikita Popov nikic commented on the diff September 12, 2012
ext/standard/password.c
((67 lines not shown))
  67
+
  68
+	return 0;
  69
+}
  70
+
  71
+static int php_password_salt_is_alphabet(const char *str, const size_t len) /* {{{ */
  72
+{
  73
+	size_t i = 0;
  74
+
  75
+	for (i = 0; i < len; i++) {
  76
+		if (!((str[i] >= 'A' && str[i] <= 'Z') || (str[i] >= 'a' && str[i] <= 'z') || (str[i] >= '0' && str[i] <= '9') || str[i] == '.' || str[i] == '/')) {
  77
+			return 0;
  78
+		}
  79
+	}
  80
+	return 1;
  81
+}
  82
+/* }}} */
2
Nikita Popov
nikic added a note September 12, 2012

I'd make this function return a zend_bool. Not that it matters much, but would make the semantics of the return value more clear.

Done. It now returns a zend_bool...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Lars Strojny lstrojny commented on the diff September 12, 2012
ext/standard/crypt.c
((33 lines not shown))
  264
+
  265
+	/* This will produce suitable results if people depend on DES-encryption
  266
+	 * available (passing always 2-character salt). At least for glibc6.1 */
  267
+	memset(&salt[1], '$', PHP_MAX_SALT_LEN - 1);
  268
+
  269
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &str, &str_len, &salt_in, &salt_in_len) == FAILURE) {
  270
+		return;
  271
+	}
  272
+
  273
+	if (salt_in) {
  274
+		memcpy(salt, salt_in, MIN(PHP_MAX_SALT_LEN, salt_in_len));
  275
+	}
  276
+
  277
+	/* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */
  278
+	if (!*salt) {
  279
+#if PHP_MD5_CRYPT
3

Does that mean that my salt will change depending on the platform?

That's core crypt() code that exists right now (as of 5.3.0). The reason for the change here is that I refactored it into two functions so I could call the implementation from C.

But now, the salt will not change (assuming that you're comment is ment for this line)...

Ah, alright. Never mind :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Anthony Ferrara

I've just added a commit to switch the algorithms definition to an ENUM (cleaner and allows for better type checking in the algos functions...

ext/standard/php_password.h
((23 lines not shown))
  23
+
  24
+PHP_FUNCTION(password_hash);
  25
+PHP_FUNCTION(password_verify);
  26
+PHP_FUNCTION(password_needs_rehash);
  27
+PHP_FUNCTION(password_get_info);
  28
+
  29
+PHP_MINIT_FUNCTION(password);
  30
+
  31
+#define PHP_PASSWORD_DEFAULT	PASSWORD_BCRYPT
  32
+
  33
+#define PHP_PASSWORD_BCRYPT_COST 10
  34
+
  35
+typedef enum {
  36
+	PASSWORD_UNKNOWN,
  37
+	PASSWORD_BCRYPT
  38
+} php_password_algos;
2
Nikita Popov
nikic added a note September 17, 2012

I'd rather call this enum php_password_algo (singular) as it fits in better with parameters and return values. Also I don't see why you removed the PHP_ prefix for the enum values. Those are global, so they should be prefixed too. (Only C++11 has proper non-global enum class values.)

Fixed by the next push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((168 lines not shown))
  168
+	efree(result);
  169
+	efree(buffer);
  170
+	ret[length] = 0;
  171
+	return SUCCESS;
  172
+}
  173
+/* }}} */
  174
+
  175
+PHP_FUNCTION(password_get_info)
  176
+{
  177
+	php_password_algos algo;
  178
+	int hash_len;
  179
+	char *hash, *algoName;
  180
+	zval *options;
  181
+
  182
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hash, &hash_len) == FAILURE) {
  183
+		RETURN_NULL();
2
Nikita Popov
nikic added a note September 17, 2012

Not that it makes a difference, but zpp failures normally use just return; instead of of any particular RETURN_* value.

Fixed by the next push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Nikita Popov nikic commented on the diff September 17, 2012
ext/standard/password.c
((160 lines not shown))
  160
+	if (php_password_salt_to64(buffer, raw_length, length, result) == FAILURE) {
  161
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Generated salt too short");
  162
+		efree(buffer);
  163
+		efree(result);
  164
+		return FAILURE;
  165
+	} else {
  166
+		memcpy(ret, result, (int) length);
  167
+	}
  168
+	efree(result);
  169
+	efree(buffer);
  170
+	ret[length] = 0;
  171
+	return SUCCESS;
  172
+}
  173
+/* }}} */
  174
+
  175
+PHP_FUNCTION(password_get_info)
2
Nikita Popov
nikic added a note September 17, 2012

password_get_info will return ['algo' => 'unknown', 'options' => []] on failure. Why was this chosen over returning false for example?

Mainly because I wanted a consistent return value. Having it always return an array seemed the better option to requiring yet another branch...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((174 lines not shown))
  174
+
  175
+PHP_FUNCTION(password_get_info)
  176
+{
  177
+	php_password_algos algo;
  178
+	int hash_len;
  179
+	char *hash, *algoName;
  180
+	zval *options;
  181
+
  182
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hash, &hash_len) == FAILURE) {
  183
+		RETURN_NULL();
  184
+	}
  185
+
  186
+	if (hash_len < 0 || (size_t) hash_len < 0) {
  187
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied Password Hash Too Long To Safely Identify");
  188
+		RETURN_FALSE;
  189
+	}
4
Nikita Popov
nikic added a note September 17, 2012

When can this happen? How can a string length be negative? And can size_t ever be smaller than int?

@nikic The string length could theoretically be negative if it exceeds the length of an int (overflow). I would rather check if hash_len is less than SIZE_MAX, but that's not defined on all systems...

Nikita Popov
nikic added a note September 17, 2012

I see what you mean. But that's a general problem that all functions accepting a string have in PHP. Not sure whether it makes sense to check for it. But if you keep the check the error message shouldn't use ucwords (just normal lowercase).

If I wasn't casting to size_t for the internal operations, I wouldn't check for it. But with the cast, I felt that it would be prudent to at least check for it... I'll fix the error message (force of habit)...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((164 lines not shown))
  164
+		return FAILURE;
  165
+	} else {
  166
+		memcpy(ret, result, (int) length);
  167
+	}
  168
+	efree(result);
  169
+	efree(buffer);
  170
+	ret[length] = 0;
  171
+	return SUCCESS;
  172
+}
  173
+/* }}} */
  174
+
  175
+PHP_FUNCTION(password_get_info)
  176
+{
  177
+	php_password_algos algo;
  178
+	int hash_len;
  179
+	char *hash, *algoName;
2
Nikita Popov
nikic added a note September 17, 2012

I can't quote anything on this, but afaik PHP is using names_with_underscores instead of camel case.

Fixed with the next push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/basic_functions.c
@@ -1854,6 +1854,25 @@
1854 1854
 ZEND_BEGIN_ARG_INFO(arginfo_getlastmod, 0)
1855 1855
 ZEND_END_ARG_INFO()
1856 1856
 /* }}} */
  1857
+/* {{{ password.c */
  1858
+ZEND_BEGIN_ARG_INFO_EX(arginfo_password_hash, 0, 0, 2)
  1859
+	ZEND_ARG_INFO(0, password)
  1860
+	ZEND_ARG_INFO(0, algo)
  1861
+	ZEND_ARG_INFO(0, options)
  1862
+ZEND_END_ARG_INFO()
  1863
+ZEND_BEGIN_ARG_INFO_EX(arginfo_password_get_info, 0, 0, 1)
  1864
+	ZEND_ARG_INFO(0, hash)
  1865
+ZEND_END_ARG_INFO()
  1866
+ZEND_BEGIN_ARG_INFO_EX(arginfo_password_needs_rehash, 0, 0, 1)
1
Nikita Popov
nikic added a note September 17, 2012

Here again the last arg should be 2 (as the algo is required too).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((230 lines not shown))
  230
+
  231
+	if (hash_len < 0) {
  232
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied password hash too long to safely identify");
  233
+		RETURN_FALSE;
  234
+	}
  235
+
  236
+	algo = php_password_determine_algo(hash, (size_t) hash_len);
  237
+	
  238
+	if (algo != new_algo) {
  239
+		RETURN_TRUE;
  240
+	}
  241
+
  242
+	switch (algo) {
  243
+		case PHP_PASSWORD_BCRYPT:
  244
+			{
  245
+				int newCost = PHP_PASSWORD_BCRYPT_COST, cost = 0;
1
Nikita Popov
nikic added a note September 17, 2012

I think those two should rather be long (as you're fetching the LVAL). Also newCost is in camel case again ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((232 lines not shown))
  232
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied password hash too long to safely identify");
  233
+		RETURN_FALSE;
  234
+	}
  235
+
  236
+	algo = php_password_determine_algo(hash, (size_t) hash_len);
  237
+	
  238
+	if (algo != new_algo) {
  239
+		RETURN_TRUE;
  240
+	}
  241
+
  242
+	switch (algo) {
  243
+		case PHP_PASSWORD_BCRYPT:
  244
+			{
  245
+				int newCost = PHP_PASSWORD_BCRYPT_COST, cost = 0;
  246
+				
  247
+				if (options && zend_symtable_find(options, "cost", 5, (void **) &option_buffer) == SUCCESS) {
1
Nikita Popov
nikic added a note September 17, 2012

Use of the symtable function is not necessary here (only required when you're taking user input as the key), could be just zend_hash_find. The 5 would be better written as sizeof("cost"), to clarify its meaning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((235 lines not shown))
  235
+
  236
+	algo = php_password_determine_algo(hash, (size_t) hash_len);
  237
+	
  238
+	if (algo != new_algo) {
  239
+		RETURN_TRUE;
  240
+	}
  241
+
  242
+	switch (algo) {
  243
+		case PHP_PASSWORD_BCRYPT:
  244
+			{
  245
+				int newCost = PHP_PASSWORD_BCRYPT_COST, cost = 0;
  246
+				
  247
+				if (options && zend_symtable_find(options, "cost", 5, (void **) &option_buffer) == SUCCESS) {
  248
+					convert_to_long_ex(option_buffer);
  249
+					newCost = Z_LVAL_PP(option_buffer);
  250
+					zval_ptr_dtor(option_buffer);
3
Nikita Popov
nikic added a note September 17, 2012

This zval_ptr_dtor isn't right here. zend_symtable_find does not add an additional ref. Though I'm a bit confused now how this should be properly done when convert_to_long_ex is used...

Nikita Popov
nikic added a note September 17, 2012

An example what would usually happen:

==7601== Invalid read of size 4
==7601==    at 0x8250A1E: _zval_ptr_dtor (zend.h:404)
==7601==    by 0x826235E: _zval_ptr_dtor_wrapper (zend_variables.c:180)
==7601==    by 0x8276453: zend_hash_destroy (zend_hash.c:560)
==7601==    by 0x8261EFB: _zval_dtor_func (zend_variables.c:43)
==7601==    by 0x82A6E58: zend_do_fcall_common_helper_SPEC (zend_variables.h:35)
==7601==    by 0x82AE0B9: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2447)
==7601==    by 0x82A453E: execute_ex (zend_vm_execute.h:440)
==7601==    by 0x82A4614: execute (zend_vm_execute.h:464)
==7601==    by 0x826665C: zend_execute_scripts (zend.c:1309)
==7601==    by 0x81C861B: php_execute_script (main.c:2459)
==7601==    by 0x83C5358: do_cli (php_cli.c:988)
==7601==    by 0x83C6893: main (php_cli.c:1364)
==7601==  Address 0x43ebe68 is 8 bytes inside a block of size 20 free'd
==7601==    at 0x402B06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==7601==    by 0x822C56A: _efree (zend_alloc.c:2433)
==7601==    by 0x8250AFD: _zval_ptr_dtor (zend_execute_API.c:440)
==7601==    by 0x81BDC94: zif_password_needs_rehash (password.c:250)
==7601==    by 0x82A5FBE: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:655)

=> The value is first freed here and later in the hash dtor.

All memory leaks that I could find have been rectified. Please test again...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((355 lines not shown))
  355
+				convert_to_string_ex(option_buffer);
  356
+				if (Z_TYPE_PP(option_buffer) == IS_STRING) {
  357
+					buffer = Z_STRVAL_PP(option_buffer);
  358
+					buffer_len_int = Z_STRLEN_PP(option_buffer);
  359
+					if (buffer_len_int < 0) {
  360
+						zval_ptr_dtor(option_buffer);
  361
+						efree(hash_format);
  362
+						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Supplied salt is too long");
  363
+					}
  364
+					buffer_len = (size_t) buffer_len_int;
  365
+					break;
  366
+				}
  367
+			case IS_RESOURCE:
  368
+			case IS_ARRAY:
  369
+			default:
  370
+				zval_ptr_dtor(option_buffer);
2
Nikita Popov
nikic added a note September 17, 2012

Again, this zval_ptr_dtor isn't right here (and also the ones in lines 360, 377 and 395). (And also again the 5 in the above symtable find call).

A much better fix has been included...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((98 lines not shown))
  98
+			ret[pos] = '.';
  99
+		} else if (buffer[pos] == '=') {
  100
+			efree(buffer);
  101
+			return FAILURE;
  102
+		} else {
  103
+			ret[pos] = buffer[pos];
  104
+		}
  105
+	}
  106
+	efree(buffer);
  107
+	return SUCCESS;
  108
+}
  109
+/* }}} */
  110
+
  111
+static int php_password_make_salt(size_t length, char *ret TSRMLS_DC) /* {{{ */
  112
+{
  113
+	int buffer_valid = 0;
1
Nikita Popov
nikic added a note September 17, 2012

This could be zend_bool too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
ext/standard/password.c
((152 lines not shown))
  152
+	}
  153
+#endif
  154
+	if (!buffer_valid) {
  155
+		for (i = 0; i < raw_length; i++) {
  156
+			buffer[i] ^= (char) (255.0 * php_rand(TSRMLS_C) / RAND_MAX);
  157
+		}
  158
+	}
  159
+
  160
+	result = safe_emalloc(length, 1, 1); 
  161
+	if (php_password_salt_to64(buffer, raw_length, length, result) == FAILURE) {
  162
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Generated salt too short");
  163
+		efree(buffer);
  164
+		efree(result);
  165
+		return FAILURE;
  166
+	} else {
  167
+		memcpy(ret, result, (int) length);
2
Nikita Popov
nikic added a note September 17, 2012

(int) cast seems unnecessary here. (Also the else clause can be dropped.)

Else clause dropped...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Nikita Popov

I'm still getting a lot of valgrind warnings, e.g.

=29932== Invalid read of size 1
==29932==    at 0x8261B61: _zval_dtor_func (zend_variables.c:35)
==29932==    by 0x82507FE: _zval_ptr_dtor (zend_variables.h:35)
==29932==    by 0x826208A: _zval_ptr_dtor_wrapper (zend_variables.c:180)
==29932==    by 0x827617F: zend_hash_destroy (zend_hash.c:560)
==29932==    by 0x8261C27: _zval_dtor_func (zend_variables.c:43)
==29932==    by 0x82A6B84: zend_do_fcall_common_helper_SPEC (zend_variables.h:35)
==29932==    by 0x82ADDE5: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2447)
==29932==    by 0x82A426A: execute_ex (zend_vm_execute.h:440)
==29932==    by 0x82A4340: execute (zend_vm_execute.h:464)
==29932==    by 0x8266388: zend_execute_scripts (zend.c:1309)
==29932==    by 0x81C8347: php_execute_script (main.c:2459)
==29932==    by 0x83C5084: do_cli (php_cli.c:988)
==29932==  Address 0x43ee77b is 3 bytes inside a block of size 4 free'd
==29932==    at 0x402B06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==29932==    by 0x822C296: _efree (zend_alloc.c:2433)
==29932==    by 0x825953C: convert_to_long_base (zend_operators.c:394)
==29932==    by 0x82593FD: convert_to_long (zend_operators.c:364)
==29932==    by 0x81BDF0E: zif_password_hash (password.c:332)
==29932==    by 0x82A5CEA: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:655)
==29932==    by 0x82ADDE5: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2447)
==29932==    by 0x82A426A: execute_ex (zend_vm_execute.h:440)
==29932==    by 0x82A4340: execute (zend_vm_execute.h:464)
==29932==    by 0x8266388: zend_execute_scripts (zend.c:1309)
==29932==    by 0x81C8347: php_execute_script (main.c:2459)
==29932==    by 0x83C5084: do_cli (php_cli.c:988)

==29932== Invalid free() / delete / delete[] / realloc()
==29932==    at 0x402B06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==29932==    by 0x822C296: _efree (zend_alloc.c:2433)
==29932==    by 0x8261BCA: _zval_dtor_func (zend_variables.c:36)
==29932==    by 0x82507FE: _zval_ptr_dtor (zend_variables.h:35)
==29932==    by 0x826208A: _zval_ptr_dtor_wrapper (zend_variables.c:180)
==29932==    by 0x827617F: zend_hash_destroy (zend_hash.c:560)
==29932==    by 0x8261C27: _zval_dtor_func (zend_variables.c:43)
==29932==    by 0x82A6B84: zend_do_fcall_common_helper_SPEC (zend_variables.h:35)
==29932==    by 0x82ADDE5: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2447)
==29932==    by 0x82A426A: execute_ex (zend_vm_execute.h:440)
==29932==    by 0x82A4340: execute (zend_vm_execute.h:464)
==29932==    by 0x8266388: zend_execute_scripts (zend.c:1309)
==29932==  Address 0x43ee778 is 0 bytes inside a block of size 4 free'd
==29932==    at 0x402B06C: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==29932==    by 0x822C296: _efree (zend_alloc.c:2433)
==29932==    by 0x825953C: convert_to_long_base (zend_operators.c:394)
==29932==    by 0x82593FD: convert_to_long (zend_operators.c:364)
==29932==    by 0x81BDF0E: zif_password_hash (password.c:332)
==29932==    by 0x82A5CEA: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:655)
==29932==    by 0x82ADDE5: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2447)
==29932==    by 0x82A426A: execute_ex (zend_vm_execute.h:440)
==29932==    by 0x82A4340: execute (zend_vm_execute.h:464)
==29932==    by 0x8266388: zend_execute_scripts (zend.c:1309)
==29932==    by 0x81C8347: php_execute_script (main.c:2459)
==29932==    by 0x83C5084: do_cli (php_cli.c:988)
==29932==
Nikita Popov

Also from a quick look at the new code, you're using INIT_PZVAL_COPY to copy the zval, but that macro doesn't invoke the copy constructor. You should use MAKE_COPY_ZVAL instead. (This might be related to the invalid reads above. The lack of copy_ctor probably causes a double free on a string.)

Anthony Ferrara
Nikita Popov

@ircmaxell I get that when running ext/standard/tests/password/password_bcrypt_errors.phpt. I compiled using --disable-all --enable-debug --enable-maintainer-zts.

Account for PHP Pull Requests php-pulls merged commit 0bc9ca3 into from October 16, 2012
Account for PHP Pull Requests php-pulls closed this October 16, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 48 unique commits by 3 authors.

Jun 24, 2012
Base structure for passsword_create and password_make_salt c77f2c2
Actually complete password_create() 7e41980
Implement password_verify 6574028
Fix memory leak on branch f7097d9
Jun 25, 2012
Basic random generator added to make_salt 18d3bd9
More error checking, and some cleaning up for password.c 618f262
Anthony Ferrara Implement openssl support for make_salt 41d7374
Refactor salt generation, rename password_create to password_hash 2d4b7cb
Jun 26, 2012
Implement php.ini setting password.bcrypt_cost 232da90
Add tests for password hashing e505316
Update tests to check ini setting 2b9591f
Add tests and error checking for large salt requested values to preve…
…nt overflow on allocation
5f44be0
Jun 27, 2012
Anthony Ferrara Fix formatting issues in password.c 0dd2f16
Jun 28, 2012
Anthony Ferrara Refactor crypt to use an external working function 6bb3865
Anthony Ferrara Refactor password.c a bit, add different error checking da3d8bf
Jun 29, 2012
Anthony Ferrara Merge remote branch 'upstream/master' into hash_password
Conflicts:
	ext/standard/crypt.c
9e18e57
Anthony Ferrara More refactoring of crypt into php_crypt, and fixing memory allocation 9c1445c
Anthony Ferrara Update password.c to use safe_emalloc in sensitive places f53112f
Jul 03, 2012
Remove php.ini setting for default bcrypt cost 6cc3c65
Some more refactoring, make algo no longer optional 6943f2a
Update signature info for changing algo to an ordinal 886527d
Jul 05, 2012
Anthony Ferrara Implement password_needs_rehash() function 5160dc1
Anthony Ferrara Fix issue with int vs long parameter db86d54
Anthony Ferrara Implement password_get_info() function ee7e799
Anthony Ferrara Cleanup whitespace issues 9d3630b
Jul 10, 2012
Anthony Ferrara Merge remote branch 'upstream/master' into hash_password
* upstream/master: (34 commits)
  Fixed Bug #62500 (Segfault in DateInterval class when extended)
  Fixed test bug #62312 (warnings changed one more time)
  fix valgrind warning
  fix valgrind warning
  fixed #62433 test for win
  update NEWS
  Fixed bug #62499 (curl_setopt($ch, CURLOPT_COOKIEFILE, "") returns false)
  appease MSVC (doesnt like unary minus of unsigned ints)
  appease MSVC (doesnt like unary minus of unsigned ints)
  appease MSVC (doesnt like unary minus of unsigned ints)
  - Fixed bug #62507 (['REQUEST_TIME'] under mod_php5 returns miliseconds instead of seconds)
  Fixed Bug #62500 (Segfault in DateInterval class when extended)
  Added in NEWS and UPGRADING for feature 55218
  Fix two issues with run-tests.php
  Fix potential integer overflow in nl2br
  Fix potential integer overflow in bin2hex
  This wil be PHP 5.3.16
  Revert change 3f3ad30: There shouldn't be new features in 5.3, especially not if they aren't in 5.4, too.
  fix (signed) integer overflow (part of bug #52550
  fix (signed) integer overflow (part of bug #52550
  ...
99b7956
Jul 11, 2012
Anthony Ferrara Switch second parameter to password_make_salt to be a flag 707c907
Aug 28, 2012
Anthony Ferrara Remove password_make_salt() from the implementation e05413c
Sep 04, 2012
Anthony Ferrara Merge remote branch 'upstream/master' into hash_password
* upstream/master: (393 commits)
  forked two tests for windows
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice)
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice).
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice).
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice)
  Fixed bug #50997 (SOAP Error when trying to submit 2nd Element of a choice)
  Bug #49510: Boolean validation fails with FILTER_NULL_ON_FAILURE with empty string or false
  Implemented ReflectionFunction::isGenerator()
  Allow null as a default value for length in mb_substr() and mb_strcut()
  Allow null as a default value for length in mb_substr() and mb_strcut()
  folder
  Initializing optional argument description in assert()
  Initializing optional argument description in assert()
  Fix test failed due to new Token T_YIELD
  fix NEWS
  Fix leak when yielding array as key
  Drop obsolete test
  Remove extra blank in notice message, should act as same as vm
  Fixed bug #62987 (Assigning to ArrayObject[null][something] overrides all undefined variables)
  assert() user message
  ...
824f1f4
Anthony Ferrara Refactoring to use size_t instead of int most places db41f9f
Sep 12, 2012
Anthony Ferrara Add tests for password_get_info and password_needs_rehash e8b7f5b
Anthony Ferrara Switch test to using strict comparison for crypt fallback e9a7bde
Anthony Ferrara Remove bcrypt_cost ini entry from declaration ebe0bd5
Anthony Ferrara Expose PASSWORD_BCRYPT_DEFAULT_COST constant and update test to use it 76f3295
Anthony Ferrara Merge remote branch 'upstream/master' into hash_password
* upstream/master:
  Generators & UPGRADING
  Capitalize the warning message
  Rearrange the codes, remove empty lines
  Provide a specific error message if date.timezone value is invalid.
  regenerate patch, and save a strlen
  Fix the wrong use of snprintf which is introduced in 1d2f619
  Fixed unintendent clearance of PHP_OUTPUT_ACTIVATED flag
  Fix doc bug #63032 (Number of release in documentation).
  fixed test for bug #52944 also for darwin
  -enable VC11 (vc2012)
  - Fixed bug #61767 (Shutdown functions not called in certain error situation) - Fixed bug #60909 (custom error handler throwing Exception + fatal error = no shutdown function)
  year++
  correct PHP version & year++
  Correct PHP version
  year++
  - fix build, there was no tsrm context there, doing a fetch but this is horribly slow, this fix needs improvement (or simply add a TSRM context in the signature in master
  - fix build, declarations must be 1st in a contextgit checkout -f master
  Fixed bug #62907 (Double free when use traits)
  Fixed bug #62991 (Segfault with generator and closure)
3e383dc
Anthony Ferrara Add news entry for password API 7161c3d
Anthony Ferrara Fix incorrect arg info required param count for password_hash 7ec80e1
Sep 13, 2012
Anthony Ferrara Switch to using an ENUM for algorithms instead of a constant 83cfff4
Sep 17, 2012
Anthony Ferrara A bunch of naming convention fixes. No functionality changes e034a46
Anthony Ferrara Fix ucwords error casing 44c2624
Anthony Ferrara Fix arg info for required params passed to needs_rehash 6fd5ba5
Anthony Ferrara Refactor slightly to enable cleaner readability 8bd79d1
Oct 05, 2012
Anthony Ferrara Fix some double free issues, and more cleanup work 4a7d18c
Anthony Ferrara Fix issue with possible memory leak 25b2d36
Oct 06, 2012
Anthony Ferrara Really fix leaks, add test cases to prove it... 1751d5f
Anthony Ferrara fix allocation and copy issue 76e83f7
Oct 07, 2012
Anthony Ferrara Clean up unreported memory leak by switching to zval_ptr_dtor 37b2207
Anthony Ferrara Refactor to using a stack based zval instead of dynamic allocation 0bc9ca3
Something went wrong with that request. Please try again.