Use proper hash_spec_result enum for return values in ext/hash (#19386)

This commit is contained in:
Alexandre Daubois 2025-08-09 12:56:40 +02:00 committed by GitHub
parent d65025b53d
commit 74c006fbab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 104 additions and 96 deletions

View file

@ -29,6 +29,10 @@ PHP 8.5 INTERNALS UPGRADE NOTES
be heap-allocated and stored in the pointer as a minimal change to keep
compatibility.
- Hash
. Hash functions now use proper hash_spec_result enum for return values
instead of using SUCCESS and FAILURE.
- Zend
. Added zend_safe_assign_to_variable_noref() function to safely assign
a value to a non-reference zval.

View file

@ -229,20 +229,20 @@ static void one_to_buffer(size_t sz, unsigned char *buf, uint64_t val) {
significant bits first. This allows 32-bit and 64-bit architectures to
interchange serialized HashContexts. */
PHP_HASH_API zend_result php_hash_serialize_spec(const php_hashcontext_object *hash, zval *zv, const char *spec) /* {{{ */
PHP_HASH_API hash_spec_result php_hash_serialize_spec(const php_hashcontext_object *hash, zval *zv, const char *spec) /* {{{ */
{
size_t pos = 0, max_alignment = 1;
unsigned char *buf = (unsigned char *) hash->context;
zval tmp;
if (buf == NULL) {
return FAILURE;
return HASH_SPEC_FAILURE;
}
array_init(zv);
while (*spec != '\0' && *spec != '.') {
char spec_ch = *spec;
size_t sz, count = parse_serialize_spec(&spec, &pos, &sz, &max_alignment);
if (pos + count * sz > hash->ops->context_size) {
return FAILURE;
return HASH_SPEC_FAILURE;
}
if (isupper((unsigned char) spec_ch)) {
pos += count * sz;
@ -265,38 +265,33 @@ PHP_HASH_API zend_result php_hash_serialize_spec(const php_hashcontext_object *h
}
}
if (*spec == '.' && align_to(pos, max_alignment) != hash->ops->context_size) {
return FAILURE;
return HASH_SPEC_FAILURE;
}
return SUCCESS;
return HASH_SPEC_SUCCESS;
}
/* }}} */
/* Unserialize a hash context serialized by `php_hash_serialize_spec` with `spec`.
Returns SUCCESS on success and a negative error code on failure.
Codes: FAILURE (-1) == generic failure
-999 == spec wrong size for context
-1000 - POS == problem at byte offset POS */
PHP_HASH_API int php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec) /* {{{ */
/* Unserialize a hash context serialized by `php_hash_serialize_spec` with `spec`. */
PHP_HASH_API hash_spec_result php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec) /* {{{ */
{
size_t pos = 0, max_alignment = 1, j = 0;
unsigned char *buf = (unsigned char *) hash->context;
zval *elt;
if (Z_TYPE_P(zv) != IS_ARRAY) {
return FAILURE;
return HASH_SPEC_FAILURE;
}
while (*spec != '\0' && *spec != '.') {
char spec_ch = *spec;
size_t sz, count = parse_serialize_spec(&spec, &pos, &sz, &max_alignment);
if (pos + count * sz > hash->ops->context_size) {
return -999;
return WRONG_CONTEXT_SIZE;
}
if (isupper((unsigned char) spec_ch)) {
pos += count * sz;
} else if (sz == 1 && count > 1) {
elt = zend_hash_index_find(Z_ARRVAL_P(zv), j);
if (!elt || Z_TYPE_P(elt) != IS_STRING || Z_STRLEN_P(elt) != count) {
return -1000 - pos;
return BYTE_OFFSET_POS_ERROR - pos;
}
++j;
memcpy(buf + pos, Z_STRVAL_P(elt), count);
@ -306,14 +301,14 @@ PHP_HASH_API int php_hash_unserialize_spec(php_hashcontext_object *hash, const z
uint64_t val;
elt = zend_hash_index_find(Z_ARRVAL_P(zv), j);
if (!elt || Z_TYPE_P(elt) != IS_LONG) {
return -1000 - pos;
return BYTE_OFFSET_POS_ERROR - pos;
}
++j;
val = (uint32_t) Z_LVAL_P(elt);
if (sz == 8) {
elt = zend_hash_index_find(Z_ARRVAL_P(zv), j);
if (!elt || Z_TYPE_P(elt) != IS_LONG) {
return -1000 - pos;
return BYTE_OFFSET_POS_ERROR - pos;
}
++j;
val += ((uint64_t) Z_LVAL_P(elt)) << 32;
@ -325,31 +320,32 @@ PHP_HASH_API int php_hash_unserialize_spec(php_hashcontext_object *hash, const z
}
}
if (*spec == '.' && align_to(pos, max_alignment) != hash->ops->context_size) {
return -999;
return WRONG_CONTEXT_SIZE;
}
return SUCCESS;
return HASH_SPEC_SUCCESS;
}
/* }}} */
PHP_HASH_API zend_result php_hash_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv) /* {{{ */
PHP_HASH_API hash_spec_result php_hash_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv) /* {{{ */
{
if (hash->ops->serialize_spec) {
*magic = PHP_HASH_SERIALIZE_MAGIC_SPEC;
return php_hash_serialize_spec(hash, zv, hash->ops->serialize_spec);
} else {
return FAILURE;
}
if (!hash->ops->serialize_spec) {
return HASH_SPEC_FAILURE;
}
*magic = PHP_HASH_SERIALIZE_MAGIC_SPEC;
return php_hash_serialize_spec(hash, zv, hash->ops->serialize_spec);
}
/* }}} */
PHP_HASH_API int php_hash_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) /* {{{ */
PHP_HASH_API hash_spec_result php_hash_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) /* {{{ */
{
if (hash->ops->serialize_spec
&& magic == PHP_HASH_SERIALIZE_MAGIC_SPEC) {
return php_hash_unserialize_spec(hash, zv, hash->ops->serialize_spec);
} else {
return FAILURE;
}
return HASH_SPEC_FAILURE;
}
/* }}} */
@ -1475,7 +1471,7 @@ PHP_METHOD(HashContext, __serialize)
ZVAL_LONG(&tmp, hash->options);
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
if (hash->ops->hash_serialize(hash, &magic, &tmp) != SUCCESS) {
if (hash->ops->hash_serialize(hash, &magic, &tmp) != HASH_SPEC_SUCCESS) {
goto serialize_failure;
}
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp);
@ -1504,7 +1500,7 @@ PHP_METHOD(HashContext, __unserialize)
HashTable *data;
zval *algo_zv, *magic_zv, *options_zv, *hash_zv, *members_zv;
zend_long magic, options;
int unserialize_result;
hash_spec_result unserialize_result;
const php_hash_ops *ops;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &data) == FAILURE) {
@ -1553,7 +1549,7 @@ PHP_METHOD(HashContext, __unserialize)
ops->hash_init(hash->context, NULL);
unserialize_result = ops->hash_unserialize(hash, magic, hash_zv);
if (unserialize_result != SUCCESS) {
if (unserialize_result != HASH_SPEC_SUCCESS) {
zend_throw_exception_ex(NULL, 0, "Incomplete or ill-formed serialization data (\"%s\" code %d)", ops->algo, unserialize_result);
/* free context */
php_hashcontext_dtor(Z_OBJ_P(object));

View file

@ -304,17 +304,17 @@ PHP_HASH_API void PHP_GOSTFinal(unsigned char digest[32], PHP_GOST_CTX *context)
ZEND_SECURE_ZERO(context, sizeof(*context));
}
static int php_gost_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
static hash_spec_result php_gost_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
{
PHP_GOST_CTX *ctx = (PHP_GOST_CTX *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
&& (r = php_hash_unserialize_spec(hash, zv, PHP_GOST_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_GOST_SPEC)) == HASH_SPEC_SUCCESS
&& ctx->length < sizeof(ctx->buffer)) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}
const php_hash_ops php_hash_gost_ops = {

View file

@ -47,7 +47,7 @@ const php_hash_ops php_hash_md4_ops = {
1
};
static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv);
static hash_spec_result php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv);
const php_hash_ops php_hash_md2_ops = {
"md2",
@ -356,15 +356,15 @@ PHP_HASH_API void PHP_MD2Final(unsigned char output[16], PHP_MD2_CTX *context)
memcpy(output, context->state, 16);
}
static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
static hash_spec_result php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
{
PHP_MD2_CTX *ctx = (PHP_MD2_CTX *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
&& (r = php_hash_unserialize_spec(hash, zv, PHP_MD2_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_MD2_SPEC)) == HASH_SPEC_SUCCESS
&& (unsigned char) ctx->in_buffer < sizeof(ctx->buffer)) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}

View file

@ -200,20 +200,20 @@ static void PHP_SHA3_Final(unsigned char* digest,
ZEND_SECURE_ZERO(ctx, sizeof(PHP_SHA3_CTX));
}
static int php_sha3_unserialize(php_hashcontext_object *hash,
static hash_spec_result php_sha3_unserialize(php_hashcontext_object *hash,
zend_long magic,
const zval *zv,
size_t block_size)
{
PHP_SHA3_CTX *ctx = (PHP_SHA3_CTX *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
&& (r = php_hash_unserialize_spec(hash, zv, PHP_SHA3_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_SHA3_SPEC)) == HASH_SPEC_SUCCESS
&& ctx->pos < block_size) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}
// ==========================================================================
@ -292,23 +292,23 @@ const php_hash_ops php_hash_sha3_##bits##_ops = { \
#endif
#define PHP_KECCAK_SPEC "b200IiIIB"
static zend_result php_keccak_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv)
static hash_spec_result php_keccak_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv)
{
*magic = PHP_HASH_SERIALIZE_MAGIC_KECCAK;
return php_hash_serialize_spec(hash, zv, PHP_KECCAK_SPEC);
}
static int php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
static hash_spec_result php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
{
Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK
&& (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == HASH_SPEC_SUCCESS
&& ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}
// ==========================================================================

View file

@ -189,17 +189,17 @@ PHP_HASH_API void PHP_SNEFRUFinal(unsigned char digest[32], PHP_SNEFRU_CTX *cont
ZEND_SECURE_ZERO(context, sizeof(*context));
}
static int php_snefru_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
static hash_spec_result php_snefru_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
{
PHP_SNEFRU_CTX *ctx = (PHP_SNEFRU_CTX *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
&& (r = php_hash_unserialize_spec(hash, zv, PHP_SNEFRU_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_SNEFRU_SPEC)) == HASH_SPEC_SUCCESS
&& ctx->length < sizeof(ctx->buffer)) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}
const php_hash_ops php_hash_snefru_ops = {

View file

@ -239,17 +239,17 @@ PHP_HASH_API void PHP_TIGER192Final(unsigned char digest[24], PHP_TIGER_CTX *con
ZEND_SECURE_ZERO(context, sizeof(*context));
}
static int php_tiger_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
static hash_spec_result php_tiger_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
{
PHP_TIGER_CTX *ctx = (PHP_TIGER_CTX *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
&& (r = php_hash_unserialize_spec(hash, zv, PHP_TIGER_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_TIGER_SPEC)) == HASH_SPEC_SUCCESS
&& ctx->length < sizeof(ctx->buffer)) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}
#define PHP_HASH_TIGER_OPS(p, b) \

View file

@ -429,20 +429,20 @@ PHP_HASH_API void PHP_WHIRLPOOLFinal(unsigned char digest[64], PHP_WHIRLPOOL_CTX
ZEND_SECURE_ZERO(context, sizeof(*context));
}
static int php_whirlpool_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
static hash_spec_result php_whirlpool_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
{
PHP_WHIRLPOOL_CTX *ctx = (PHP_WHIRLPOOL_CTX *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
&& (r = php_hash_unserialize_spec(hash, zv, PHP_WHIRLPOOL_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_WHIRLPOOL_SPEC)) == HASH_SPEC_SUCCESS
&& ctx->buffer.pos >= 0
&& ctx->buffer.pos < (int) sizeof(ctx->buffer.data)
&& ctx->buffer.bits >= ctx->buffer.pos * 8
&& ctx->buffer.bits < ctx->buffer.pos * 8 + 8) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}
const php_hash_ops php_hash_whirlpool_ops = {

View file

@ -17,9 +17,9 @@
#include "php_hash.h"
#include "php_hash_xxhash.h"
static int php_hash_xxh32_unserialize(
static hash_spec_result php_hash_xxh32_unserialize(
php_hashcontext_object *hash, zend_long magic, const zval *zv);
static int php_hash_xxh64_unserialize(
static hash_spec_result php_hash_xxh64_unserialize(
php_hashcontext_object *hash, zend_long magic, const zval *zv);
const php_hash_ops php_hash_xxh32_ops = {
@ -75,18 +75,18 @@ PHP_HASH_API zend_result PHP_XXH32Copy(const php_hash_ops *ops, const PHP_XXH32_
return SUCCESS;
}
static int php_hash_xxh32_unserialize(
static hash_spec_result php_hash_xxh32_unserialize(
php_hashcontext_object *hash, zend_long magic, const zval *zv)
{
PHP_XXH32_CTX *ctx = (PHP_XXH32_CTX *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
&& (r = php_hash_unserialize_spec(hash, zv, PHP_XXH32_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_XXH32_SPEC)) == HASH_SPEC_SUCCESS
&& ctx->s.memsize < 16) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}
const php_hash_ops php_hash_xxh64_ops = {
@ -231,18 +231,18 @@ PHP_HASH_API zend_result PHP_XXH3_64_Copy(const php_hash_ops *ops, const PHP_XXH
return SUCCESS;
}
static int php_hash_xxh64_unserialize(
static hash_spec_result php_hash_xxh64_unserialize(
php_hashcontext_object *hash, zend_long magic, const zval *zv)
{
PHP_XXH64_CTX *ctx = (PHP_XXH64_CTX *) hash->context;
int r = FAILURE;
hash_spec_result r = HASH_SPEC_FAILURE;
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
&& (r = php_hash_unserialize_spec(hash, zv, PHP_XXH64_SPEC)) == SUCCESS
&& (r = php_hash_unserialize_spec(hash, zv, PHP_XXH64_SPEC)) == HASH_SPEC_SUCCESS
&& ctx->s.memsize < 32) {
return SUCCESS;
} else {
return r != SUCCESS ? r : -2000;
return HASH_SPEC_SUCCESS;
}
return r != HASH_SPEC_SUCCESS ? r : CONTEXT_VALIDATION_FAILURE;
}
const php_hash_ops php_hash_xxh3_128_ops = {

View file

@ -29,14 +29,22 @@
#define L64 INT64_C
typedef enum {
HASH_SPEC_SUCCESS = 0,
HASH_SPEC_FAILURE = -1,
WRONG_CONTEXT_SIZE = -999,
BYTE_OFFSET_POS_ERROR = -1000,
CONTEXT_VALIDATION_FAILURE = -2000,
} hash_spec_result;
typedef struct _php_hashcontext_object php_hashcontext_object;
typedef void (*php_hash_init_func_t)(void *context, HashTable *args);
typedef void (*php_hash_update_func_t)(void *context, const unsigned char *buf, size_t count);
typedef void (*php_hash_final_func_t)(unsigned char *digest, void *context);
typedef zend_result (*php_hash_copy_func_t)(const void *ops, const void *orig_context, void *dest_context);
typedef zend_result (*php_hash_serialize_func_t)(const php_hashcontext_object *hash, zend_long *magic, zval *zv);
typedef int (*php_hash_unserialize_func_t)(php_hashcontext_object *hash, zend_long magic, const zval *zv);
typedef hash_spec_result (*php_hash_serialize_func_t)(const php_hashcontext_object *hash, zend_long *magic, zval *zv);
typedef hash_spec_result (*php_hash_unserialize_func_t)(php_hashcontext_object *hash, zend_long magic, const zval *zv);
typedef struct _php_hash_ops {
const char *algo;
@ -148,10 +156,10 @@ extern PHP_HASH_API zend_class_entry *php_hashcontext_ce;
PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(zend_string *algo);
PHP_HASH_API void php_hash_register_algo(const char *algo, const php_hash_ops *ops);
PHP_HASH_API zend_result php_hash_copy(const void *ops, const void *orig_context, void *dest_context);
PHP_HASH_API zend_result php_hash_serialize(const php_hashcontext_object *context, zend_long *magic, zval *zv);
PHP_HASH_API int php_hash_unserialize(php_hashcontext_object *context, zend_long magic, const zval *zv);
PHP_HASH_API zend_result php_hash_serialize_spec(const php_hashcontext_object *context, zval *zv, const char *spec);
PHP_HASH_API int php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec);
PHP_HASH_API hash_spec_result php_hash_serialize(const php_hashcontext_object *context, zend_long *magic, zval *zv);
PHP_HASH_API hash_spec_result php_hash_unserialize(php_hashcontext_object *context, zend_long magic, const zval *zv);
PHP_HASH_API hash_spec_result php_hash_serialize_spec(const php_hashcontext_object *context, zval *zv, const char *spec);
PHP_HASH_API hash_spec_result php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec);
static inline void *php_hash_alloc_context(const php_hash_ops *ops) {
/* Zero out context memory so serialization doesn't expose internals */