While exploring password_*() functions, I found myself in php_crypt()
and noticed a use-after-free in the PHP_USE_PHP_CRYPT_R==0 case.
char *crypt_res;
{
#if something
struct crypt_data buffer;
#else
CRYPTD buffer;
#endif
crypt_res = crypt_r(password, salt, &buffer);
}
return zend_string_init(crypt_res, strlen(crypt_res), 0);
The above looks fine, until you realize that crypt_r() returns a
pointer to a portion of buffer
. Buffer has fallen out of scope
though, so according to spec, it's undefined as to whether or not the
memory address will still be trustable. In practice, gcc is okay, but
again, not due to defined behavior.
But wait, there's more. I started moving a few things around to fix
this (which turned into something of a refactor) and when I ran the
tests, everything was fine. But of course, my build has
PHP_USE_PHP_CRYPT_R==1 (as many do, I suspect). So I rebuilt and ran
the tests manually flipping PHP_USE_PHP_CRYPT_R to Zero. and the
following tests now fail:
Test DES with invalid fallback
[ext/standard/tests/crypt/des_fallback_invalid_salt.phpt]
Test deprecated operation of password_hash()
[ext/standard/tests/password/password_deprecated_salts.phpt]
Test normal operation of password_hash()
[ext/standard/tests/password/password_hash.phpt]
Test normal operation of password_verify)
[ext/standard/tests/password/password_verify.phpt]
Bug #73058 crypt broken when salt is 'too' long
[ext/standard/tests/strings/bug73058.phpt]
crypt()
function [ext/standard/tests/strings/crypt.phpt]
Official blowfish tests [ext/standard/tests/strings/crypt_blowfish.phpt]
crypt()
function - characters > 0x80
[ext/standard/tests/strings/crypt_chars.phpt]
crypt()
: *0 should return *1 [ext/standard/tests/strings/crypt_des_error.phpt]
In some cases, this is just a matter of the two implementations
falling out of sync, however a few come down to actual behavioral
differences between php_crypt_r() and crypt_r(). This may, of course,
be down to my crypt()
being broken somehow and that's why my build
is using the more portable php_crypt_r() implementation...
TL;DR - Do we have any idea (maybe from QA reports?) how many people
are even /using/ system crypt()
, and does it make any kind of sense to
just remove support for it (and all the myriad #ifdef checks) given
that we have a much more portable implementation?
-Sara