1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
From 18dc2aacd6ab25c2aaf92a43d32d8eaf4235dcc0 Mon Sep 17 00:00:00 2001
From: Michael Grunder <michael.grunder@gmail.com>
Date: Tue, 6 Nov 2018 08:46:02 -0800
Subject: [PATCH] 32bit xclaim fix (#1444)
This should fix the XCLAIM issue on 32-bit PHP installs.
This change will allow the user to pass the XCLAIM TIME option pretty much any way they want (string, long, or float) and it should work. Note that in 32-bit PHP they will only be able to pass exact values <= 2^53 as PHP will use a double precision floating point for integer overflows.
---
common.h | 2 ++
library.c | 16 +++++++++----
library.h | 1 +
redis_commands.c | 59 ++++++++++++++++++++++++++++++++++++++++--------
4 files changed, 63 insertions(+), 15 deletions(-)
diff --git a/common.h b/common.h
index 0771cfd7..adbe3035 100644
--- a/common.h
+++ b/common.h
@@ -461,8 +461,10 @@ typedef size_t strlen_t;
#ifdef PHP_WIN32
#define PHP_REDIS_API __declspec(dllexport)
+#define phpredis_atoi64(p) _atoi64((p))
#else
#define PHP_REDIS_API
+#define phpredis_atoi64(p) atoll((p))
#endif
/* reply types */
diff --git a/library.c b/library.c
index a6c93f9d..00d2f7ab 100644
--- a/library.c
+++ b/library.c
@@ -698,6 +698,15 @@ int redis_cmd_append_sstr_long(smart_string *str, long append) {
return redis_cmd_append_sstr(str, long_buf, long_len);
}
+/*
+ * Append a 64-bit integer to our command
+ */
+int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) {
+ char nbuf[64];
+ int len = snprintf(nbuf, sizeof(nbuf), "%lld", append);
+ return redis_cmd_append_sstr(str, nbuf, len);
+}
+
/*
* Append a double to a smart string command
*/
@@ -1080,11 +1089,8 @@ PHP_REDIS_API void redis_long_response(INTERNAL_FUNCTION_PARAMETERS,
}
if(response[0] == ':') {
-#ifdef PHP_WIN32
- __int64 ret = _atoi64(response + 1);
-#else
- long long ret = atoll(response + 1);
-#endif
+ int64_t ret = phpredis_atoi64(response + 1);
+
if (IS_ATOMIC(redis_sock)) {
if(ret > LONG_MAX) { /* overflow */
RETVAL_STRINGL(response + 1, response_len - 1);
diff --git a/library.h b/library.h
index 1c936dd5..80b12d03 100644
--- a/library.h
+++ b/library.h
@@ -18,6 +18,7 @@ int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyw
int redis_cmd_append_sstr(smart_string *str, char *append, int append_len);
int redis_cmd_append_sstr_int(smart_string *str, int append);
int redis_cmd_append_sstr_long(smart_string *str, long append);
+int redis_cmd_append_sstr_i64(smart_string *str, int64_t append);
int redis_cmd_append_sstr_dbl(smart_string *str, double value);
int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock TSRMLS_DC);
int redis_cmd_append_sstr_key(smart_string *str, char *key, strlen_t len, RedisSock *redis_sock, short *slot);
diff --git a/redis_commands.c b/redis_commands.c
index 14fb56f2..1ef216d4 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -3525,13 +3525,56 @@ int redis_xack_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
typedef struct xclaimOptions {
struct {
char *type;
- zend_long time;
+ int64_t time;
} idle;
zend_long retrycount;
int force;
int justid;
} xclaimOptions;
+/* Attempt to extract an int64_t from the provided zval */
+static int zval_get_i64(zval *zv, int64_t *retval) {
+ if (Z_TYPE_P(zv) == IS_LONG) {
+ *retval = (int64_t)Z_LVAL_P(zv);
+ return SUCCESS;
+ } else if (Z_TYPE_P(zv) == IS_DOUBLE) {
+ *retval = (int64_t)Z_DVAL_P(zv);
+ return SUCCESS;
+ } else if (Z_TYPE_P(zv) == IS_STRING) {
+ zend_long lval;
+ double dval;
+
+ switch (is_numeric_string(Z_STRVAL_P(zv), Z_STRLEN_P(zv), &lval, &dval, 1)) {
+ case IS_LONG:
+ *retval = (int64_t)lval;
+ return SUCCESS;
+ case IS_DOUBLE:
+ *retval = (int64_t)dval;
+ return SUCCESS;
+ }
+ }
+
+ /* If we make it here we have failed */
+ return FAILURE;
+}
+
+/* Helper function to get an integer XCLAIM argument. This can overflow a
+ * 32-bit PHP long so we have to extract it as an int64_t. If the value is
+ * not a valid number or negative, we'll inform the user of the problem and
+ * that the argument is being ignored. */
+static int64_t get_xclaim_i64_arg(const char *key, zval *zv TSRMLS_DC) {
+ int64_t retval = -1;
+
+ /* Extract an i64, and if we can't let the user know there is an issue. */
+ if (zval_get_i64(zv, &retval) == FAILURE || retval < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING,
+ "Invalid XCLAIM option '%s' will be ignored", key);
+ }
+
+ /* Success */
+ return retval;
+}
+
/* Helper to extract XCLAIM options */
static void get_xclaim_options(zval *z_arr, xclaimOptions *opt TSRMLS_DC) {
HashTable *ht;
@@ -3556,23 +3599,19 @@ static void get_xclaim_options(zval *z_arr, xclaimOptions *opt TSRMLS_DC) {
ht = Z_ARRVAL_P(z_arr);
ZEND_HASH_FOREACH_KEY_VAL(ht, idx, zkey, zv) {
if (zkey) {
- /* Every key => value xclaim option requires a long and Redis
- * treats -1 as not being passed so skip negative values too. */
- if (Z_TYPE_P(zv) != IS_LONG || Z_LVAL_P(zv) < 0)
- continue;
-
kval = ZSTR_VAL(zkey);
klen = ZSTR_LEN(zkey);
+
if (klen == 4) {
if (!strncasecmp(kval, "TIME", 4)) {
opt->idle.type = "TIME";
- opt->idle.time = Z_LVAL_P(zv);
+ opt->idle.time = get_xclaim_i64_arg("TIME", zv TSRMLS_CC);
} else if (!strncasecmp(kval, "IDLE", 4)) {
opt->idle.type = "IDLE";
- opt->idle.time = Z_LVAL_P(zv);
+ opt->idle.time = get_xclaim_i64_arg("IDLE", zv TSRMLS_CC);
}
} else if (klen == 10 && !strncasecmp(kval, "RETRYCOUNT", 10)) {
- opt->retrycount = Z_LVAL_P(zv);
+ opt->retrycount = zval_get_long(zv);
}
} else {
if (Z_TYPE_P(zv) == IS_STRING) {
@@ -3609,7 +3648,7 @@ static void append_xclaim_options(smart_string *cmd, xclaimOptions *opt) {
/* IDLE/TIME long */
if (opt->idle.type != NULL && opt->idle.time != -1) {
redis_cmd_append_sstr(cmd, opt->idle.type, strlen(opt->idle.type));
- redis_cmd_append_sstr_long(cmd, opt->idle.time);
+ redis_cmd_append_sstr_i64(cmd, opt->idle.time);
}
/* RETRYCOUNT */
|