Coverage Report

Created: 2025-06-13 06:43

/src/php-src/ext/hash/hash_xxhash.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | https://www.php.net/license/3_01.txt                                 |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Author: Anatol Belski <ab@php.net>                                   |
14
   +----------------------------------------------------------------------+
15
*/
16
17
#include "php_hash.h"
18
#include "php_hash_xxhash.h"
19
20
static int php_hash_xxh32_unserialize(
21
    php_hashcontext_object *hash, zend_long magic, const zval *zv);
22
static int php_hash_xxh64_unserialize(
23
    php_hashcontext_object *hash, zend_long magic, const zval *zv);
24
25
const php_hash_ops php_hash_xxh32_ops = {
26
  "xxh32",
27
  (php_hash_init_func_t) PHP_XXH32Init,
28
  (php_hash_update_func_t) PHP_XXH32Update,
29
  (php_hash_final_func_t) PHP_XXH32Final,
30
  (php_hash_copy_func_t) PHP_XXH32Copy,
31
  php_hash_serialize,
32
  php_hash_xxh32_unserialize,
33
  PHP_XXH32_SPEC,
34
  4,
35
  4,
36
  sizeof(PHP_XXH32_CTX),
37
  0
38
};
39
40
PHP_HASH_API void PHP_XXH32Init(PHP_XXH32_CTX *ctx, HashTable *args)
41
108
{
42
  /* XXH32_createState() is not used intentionally. */
43
108
  memset(&ctx->s, 0, sizeof ctx->s);
44
45
108
  if (args) {
46
0
    zval *seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
47
    /* This might be a bit too restrictive, but thinking that a seed might be set
48
      once and for all, it should be done a clean way. */
49
0
    if (seed) {
50
0
      if (IS_LONG == Z_TYPE_P(seed)) {
51
0
        XXH32_reset(&ctx->s, (XXH32_hash_t)Z_LVAL_P(seed));
52
0
        return;
53
0
      } else {
54
0
        php_error_docref(NULL, E_DEPRECATED, "Passing a seed of a type other than int is deprecated because it is the same as setting the seed to 0");
55
0
      }
56
0
    }
57
0
  }
58
59
108
  XXH32_reset(&ctx->s, 0);
60
108
}
61
62
PHP_HASH_API void PHP_XXH32Update(PHP_XXH32_CTX *ctx, const unsigned char *in, size_t len)
63
82
{
64
82
  XXH32_update(&ctx->s, in, len);
65
82
}
66
67
PHP_HASH_API void PHP_XXH32Final(unsigned char digest[4], PHP_XXH32_CTX *ctx)
68
82
{
69
82
  XXH32_canonicalFromHash((XXH32_canonical_t*)digest, XXH32_digest(&ctx->s));
70
82
}
71
72
PHP_HASH_API zend_result PHP_XXH32Copy(const php_hash_ops *ops, const PHP_XXH32_CTX *orig_context, PHP_XXH32_CTX *copy_context)
73
0
{
74
0
  copy_context->s = orig_context->s;
75
0
  return SUCCESS;
76
0
}
77
78
static int php_hash_xxh32_unserialize(
79
    php_hashcontext_object *hash, zend_long magic, const zval *zv)
80
108
{
81
108
  PHP_XXH32_CTX *ctx = (PHP_XXH32_CTX *) hash->context;
82
108
  int r = FAILURE;
83
108
  if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
84
108
    && (r = php_hash_unserialize_spec(hash, zv, PHP_XXH32_SPEC)) == SUCCESS
85
108
    && ctx->s.memsize < 16) {
86
82
    return SUCCESS;
87
82
  } else {
88
26
    return r != SUCCESS ? r : -2000;
89
26
  }
90
108
}
91
92
const php_hash_ops php_hash_xxh64_ops = {
93
  "xxh64",
94
  (php_hash_init_func_t) PHP_XXH64Init,
95
  (php_hash_update_func_t) PHP_XXH64Update,
96
  (php_hash_final_func_t) PHP_XXH64Final,
97
  (php_hash_copy_func_t) PHP_XXH64Copy,
98
  php_hash_serialize,
99
  php_hash_xxh64_unserialize,
100
  PHP_XXH64_SPEC,
101
  8,
102
  8,
103
  sizeof(PHP_XXH64_CTX),
104
  0
105
};
106
107
PHP_HASH_API void PHP_XXH64Init(PHP_XXH64_CTX *ctx, HashTable *args)
108
70
{
109
  /* XXH64_createState() is not used intentionally. */
110
70
  memset(&ctx->s, 0, sizeof ctx->s);
111
112
70
  if (args) {
113
0
    zval *seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
114
    /* This might be a bit too restrictive, but thinking that a seed might be set
115
      once and for all, it should be done a clean way. */
116
0
    if (seed && IS_LONG == Z_TYPE_P(seed)) {
117
0
      XXH64_reset(&ctx->s, (XXH64_hash_t)Z_LVAL_P(seed));
118
0
      return;
119
0
    } else {
120
0
      php_error_docref(NULL, E_DEPRECATED, "Passing a seed of a type other than int is deprecated because it is the same as setting the seed to 0");
121
0
    }
122
0
  }
123
124
70
  XXH64_reset(&ctx->s, 0);
125
70
}
126
127
PHP_HASH_API void PHP_XXH64Update(PHP_XXH64_CTX *ctx, const unsigned char *in, size_t len)
128
58
{
129
58
  XXH64_update(&ctx->s, in, len);
130
58
}
131
132
PHP_HASH_API void PHP_XXH64Final(unsigned char digest[8], PHP_XXH64_CTX *ctx)
133
58
{
134
58
  XXH64_canonicalFromHash((XXH64_canonical_t*)digest, XXH64_digest(&ctx->s));
135
58
}
136
137
PHP_HASH_API zend_result PHP_XXH64Copy(const php_hash_ops *ops, const PHP_XXH64_CTX *orig_context, PHP_XXH64_CTX *copy_context)
138
0
{
139
0
  copy_context->s = orig_context->s;
140
0
  return SUCCESS;
141
0
}
142
143
const php_hash_ops php_hash_xxh3_64_ops = {
144
  "xxh3",
145
  (php_hash_init_func_t) PHP_XXH3_64_Init,
146
  (php_hash_update_func_t) PHP_XXH3_64_Update,
147
  (php_hash_final_func_t) PHP_XXH3_64_Final,
148
  (php_hash_copy_func_t) PHP_XXH3_64_Copy,
149
  php_hash_serialize,
150
  php_hash_unserialize,
151
  NULL,
152
  8,
153
  8,
154
  sizeof(PHP_XXH3_64_CTX),
155
  0
156
};
157
158
typedef XXH_errorcode (*xxh3_reset_with_secret_func_t)(XXH3_state_t*, const void*, size_t);
159
typedef XXH_errorcode (*xxh3_reset_with_seed_func_t)(XXH3_state_t*, XXH64_hash_t);
160
161
static void _PHP_XXH3_Init(PHP_XXH3_64_CTX *ctx, HashTable *args,
162
    xxh3_reset_with_seed_func_t func_init_seed, xxh3_reset_with_secret_func_t func_init_secret, const char* algo_name)
163
0
{
164
0
  memset(&ctx->s, 0, sizeof ctx->s);
165
166
0
  if (args) {
167
0
    zval *_seed = zend_hash_str_find_deref(args, "seed", sizeof("seed") - 1);
168
0
    zval *_secret = zend_hash_str_find_deref(args, "secret", sizeof("secret") - 1);
169
170
0
    if (_seed && _secret) {
171
0
      zend_throw_error(NULL, "%s: Only one of seed or secret is to be passed for initialization", algo_name);
172
0
      return;
173
0
    }
174
175
0
    if (_seed && IS_LONG != Z_TYPE_P(_seed)) {
176
0
      php_error_docref(NULL, E_DEPRECATED, "Passing a seed of a type other than int is deprecated because it is ignored");
177
0
    }
178
179
0
    if (_seed && IS_LONG == Z_TYPE_P(_seed)) {
180
      /* This might be a bit too restrictive, but thinking that a seed might be set
181
        once and for all, it should be done a clean way. */
182
0
      func_init_seed(&ctx->s, (XXH64_hash_t)Z_LVAL_P(_seed));
183
0
      return;
184
0
    } else if (_secret) {
185
0
      if (IS_STRING != Z_TYPE_P(_secret)) {
186
0
        php_error_docref(NULL, E_DEPRECATED, "Passing a secret of a type other than string is deprecated because it implicitly converts to a string, potentially hiding bugs");
187
0
      }
188
0
      zend_string *secret_string = zval_try_get_string(_secret);
189
0
      if (UNEXPECTED(!secret_string)) {
190
0
        ZEND_ASSERT(EG(exception));
191
0
        return;
192
0
      }
193
0
      size_t len = ZSTR_LEN(secret_string);
194
0
      if (len < PHP_XXH3_SECRET_SIZE_MIN) {
195
0
        zend_string_release(secret_string);
196
0
        zend_throw_error(NULL, "%s: Secret length must be >= %u bytes, %zu bytes passed", algo_name, XXH3_SECRET_SIZE_MIN, len);
197
0
        return;
198
0
      }
199
0
      if (len > sizeof(ctx->secret)) {
200
0
        len = sizeof(ctx->secret);
201
0
        php_error_docref(NULL, E_WARNING, "%s: Secret content exceeding %zu bytes discarded", algo_name, sizeof(ctx->secret));
202
0
      }
203
0
      memcpy((unsigned char *)ctx->secret, ZSTR_VAL(secret_string), len);
204
0
      zend_string_release(secret_string);
205
0
      func_init_secret(&ctx->s, ctx->secret, len);
206
0
      return;
207
0
    }
208
0
  }
209
210
0
  func_init_seed(&ctx->s, 0);
211
0
}
212
213
PHP_HASH_API void PHP_XXH3_64_Init(PHP_XXH3_64_CTX *ctx, HashTable *args)
214
0
{
215
0
  _PHP_XXH3_Init(ctx, args, XXH3_64bits_reset_withSeed, XXH3_64bits_reset_withSecret, "xxh3");
216
0
}
217
218
PHP_HASH_API void PHP_XXH3_64_Update(PHP_XXH3_64_CTX *ctx, const unsigned char *in, size_t len)
219
0
{
220
0
  XXH3_64bits_update(&ctx->s, in, len);
221
0
}
222
223
PHP_HASH_API void PHP_XXH3_64_Final(unsigned char digest[8], PHP_XXH3_64_CTX *ctx)
224
0
{
225
0
  XXH64_canonicalFromHash((XXH64_canonical_t*)digest, XXH3_64bits_digest(&ctx->s));
226
0
}
227
228
PHP_HASH_API zend_result PHP_XXH3_64_Copy(const php_hash_ops *ops, const PHP_XXH3_64_CTX *orig_context, PHP_XXH3_64_CTX *copy_context)
229
0
{
230
0
  copy_context->s = orig_context->s;
231
0
  return SUCCESS;
232
0
}
233
234
static int php_hash_xxh64_unserialize(
235
    php_hashcontext_object *hash, zend_long magic, const zval *zv)
236
70
{
237
70
  PHP_XXH64_CTX *ctx = (PHP_XXH64_CTX *) hash->context;
238
70
  int r = FAILURE;
239
70
  if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
240
70
    && (r = php_hash_unserialize_spec(hash, zv, PHP_XXH64_SPEC)) == SUCCESS
241
70
    && ctx->s.memsize < 32) {
242
58
    return SUCCESS;
243
58
  } else {
244
12
    return r != SUCCESS ? r : -2000;
245
12
  }
246
70
}
247
248
const php_hash_ops php_hash_xxh3_128_ops = {
249
  "xxh128",
250
  (php_hash_init_func_t) PHP_XXH3_128_Init,
251
  (php_hash_update_func_t) PHP_XXH3_128_Update,
252
  (php_hash_final_func_t) PHP_XXH3_128_Final,
253
  (php_hash_copy_func_t) PHP_XXH3_128_Copy,
254
  php_hash_serialize,
255
  php_hash_unserialize,
256
  NULL,
257
  16,
258
  8,
259
  sizeof(PHP_XXH3_128_CTX),
260
  0
261
};
262
263
PHP_HASH_API void PHP_XXH3_128_Init(PHP_XXH3_128_CTX *ctx, HashTable *args)
264
0
{
265
0
  _PHP_XXH3_Init(ctx, args, XXH3_128bits_reset_withSeed, XXH3_128bits_reset_withSecret, "xxh128");
266
0
}
267
268
PHP_HASH_API void PHP_XXH3_128_Update(PHP_XXH3_128_CTX *ctx, const unsigned char *in, size_t len)
269
0
{
270
0
  XXH3_128bits_update(&ctx->s, in, len);
271
0
}
272
273
PHP_HASH_API void PHP_XXH3_128_Final(unsigned char digest[16], PHP_XXH3_128_CTX *ctx)
274
0
{
275
0
  XXH128_canonicalFromHash((XXH128_canonical_t*)digest, XXH3_128bits_digest(&ctx->s));
276
0
}
277
278
PHP_HASH_API zend_result PHP_XXH3_128_Copy(const php_hash_ops *ops, const PHP_XXH3_128_CTX *orig_context, PHP_XXH3_128_CTX *copy_context)
279
0
{
280
0
  copy_context->s = orig_context->s;
281
0
  return SUCCESS;
282
0
}
283