Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
60 changes: 28 additions & 32 deletions ext/hash/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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;
}
/* }}} */

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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));
Expand Down
12 changes: 6 additions & 6 deletions ext/hash/hash_gost.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
14 changes: 7 additions & 7 deletions ext/hash/hash_md.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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;
}
26 changes: 13 additions & 13 deletions ext/hash/hash_sha3.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

// ==========================================================================
Expand Down Expand Up @@ -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;
}

// ==========================================================================
Expand Down
12 changes: 6 additions & 6 deletions ext/hash/hash_snefru.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
12 changes: 6 additions & 6 deletions ext/hash/hash_tiger.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
12 changes: 6 additions & 6 deletions ext/hash/hash_whirlpool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Loading
Loading