php_crypt() problems

July 15, 2017 21:09 (Sara Golemon)
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;
  CRYPTD buffer;
  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
Test deprecated operation of password_hash()
Test normal operation of password_hash()
Test normal operation of password_verify)
Bug #73058 crypt broken when salt is 'too' long
crypt() function [ext/standard/tests/strings/crypt.phpt]
Official blowfish tests [ext/standard/tests/strings/crypt_blowfish.phpt]
crypt() function - characters > 0x80
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?