Skip to content

Commit eb063c8

Browse files
committed
Fixed bug #77612
Port php_setcookie() to use the smart_str API to ensure that there can be no string truncation issues.
1 parent 7bc162f commit eb063c8

File tree

2 files changed

+37
-51
lines changed

2 files changed

+37
-51
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ PHP NEWS
3636
- Standard:
3737
. Fixed bug #77552 (Unintialized php_stream_statbuf in stat functions).
3838
(John Stevenson)
39+
. Fixed bug #77612 (setcookie() sets incorrect SameSite header if all of its
40+
options filled). (Nikita)
3941

4042
- MySQL
4143
. Disabled LOCAL INFILE by default, can be enabled using php.ini directive

ext/standard/head.c

Lines changed: 35 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#endif
3131

3232
#include "php_globals.h"
33+
#include "zend_smart_str.h"
3334

3435

3536
/* Implementation of the language Header() function */
@@ -81,12 +82,10 @@ PHPAPI int php_header(void)
8182

8283
PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, int secure, int httponly, zend_string *samesite, int url_encode)
8384
{
84-
char *cookie;
85-
size_t len = sizeof("Set-Cookie: ");
8685
zend_string *dt;
8786
sapi_header_line ctr = {0};
8887
int result;
89-
zend_string *encoded_value = NULL;
88+
smart_str buf = {0};
9089

9190
if (!ZSTR_LEN(name)) {
9291
zend_error( E_WARNING, "Cookie names must not be empty" );
@@ -112,97 +111,82 @@ PHPAPI int php_setcookie(zend_string *name, zend_string *value, time_t expires,
112111
return FAILURE;
113112
}
114113

115-
len += ZSTR_LEN(name);
116-
if (value) {
117-
if (url_encode) {
118-
encoded_value = php_url_encode(ZSTR_VAL(value), ZSTR_LEN(value));
119-
len += ZSTR_LEN(encoded_value);
120-
} else {
121-
encoded_value = zend_string_copy(value);
122-
len += ZSTR_LEN(encoded_value);
123-
}
124-
}
125-
126-
if (path) {
127-
len += ZSTR_LEN(path);
128-
}
129-
if (domain) {
130-
len += ZSTR_LEN(domain);
131-
}
132-
if (samesite) {
133-
len += ZSTR_LEN(samesite);
134-
}
135-
136-
cookie = emalloc(len + 100);
137-
138114
if (value == NULL || ZSTR_LEN(value) == 0) {
139115
/*
140116
* MSIE doesn't delete a cookie when you set it to a null value
141117
* so in order to force cookies to be deleted, even on MSIE, we
142118
* pick an expiry date in the past
143119
*/
144120
dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, 1, 0);
145-
snprintf(cookie, len + 100, "Set-Cookie: %s=deleted; expires=%s; Max-Age=0", ZSTR_VAL(name), ZSTR_VAL(dt));
121+
smart_str_appends(&buf, "Set-Cookie: ");
122+
smart_str_append(&buf, name);
123+
smart_str_appends(&buf, "=deleted; expires=");
124+
smart_str_append(&buf, dt);
125+
smart_str_appends(&buf, "; Max-Age=0");
146126
zend_string_free(dt);
147127
} else {
148-
snprintf(cookie, len + 100, "Set-Cookie: %s=%s", ZSTR_VAL(name), value ? ZSTR_VAL(encoded_value) : "");
128+
smart_str_appends(&buf, "Set-Cookie: ");
129+
smart_str_append(&buf, name);
130+
smart_str_appendc(&buf, '=');
131+
if (url_encode) {
132+
zend_string *encoded_value = php_url_encode(ZSTR_VAL(value), ZSTR_LEN(value));
133+
smart_str_append(&buf, encoded_value);
134+
zend_string_release_ex(encoded_value, 0);
135+
} else {
136+
smart_str_append(&buf, value);
137+
}
149138
if (expires > 0) {
150139
const char *p;
151-
char tsdelta[13];
152140
double diff;
153141

154-
strlcat(cookie, COOKIE_EXPIRES, len + 100);
142+
smart_str_appends(&buf, COOKIE_EXPIRES);
155143
dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0);
156144
/* check to make sure that the year does not exceed 4 digits in length */
157145
p = zend_memrchr(ZSTR_VAL(dt), '-', ZSTR_LEN(dt));
158146
if (!p || *(p + 5) != ' ') {
159147
zend_string_free(dt);
160-
efree(cookie);
161-
zend_string_release_ex(encoded_value, 0);
148+
smart_str_free(&buf);
162149
zend_error(E_WARNING, "Expiry date cannot have a year greater than 9999");
163150
return FAILURE;
164151
}
165-
strlcat(cookie, ZSTR_VAL(dt), len + 100);
152+
153+
smart_str_append(&buf, dt);
166154
zend_string_free(dt);
167155

168156
diff = difftime(expires, time(NULL));
169157
if (diff < 0) {
170158
diff = 0;
171159
}
172-
snprintf(tsdelta, sizeof(tsdelta), ZEND_LONG_FMT, (zend_long) diff);
173-
strlcat(cookie, COOKIE_MAX_AGE, len + 100);
174-
strlcat(cookie, tsdelta, len + 100);
175-
}
176-
}
177160

178-
if (encoded_value) {
179-
zend_string_release_ex(encoded_value, 0);
161+
smart_str_appends(&buf, COOKIE_MAX_AGE);
162+
smart_str_append_long(&buf, (zend_long) diff);
163+
}
180164
}
181165

182166
if (path && ZSTR_LEN(path)) {
183-
strlcat(cookie, COOKIE_PATH, len + 100);
184-
strlcat(cookie, ZSTR_VAL(path), len + 100);
167+
smart_str_appends(&buf, COOKIE_PATH);
168+
smart_str_append(&buf, path);
185169
}
186170
if (domain && ZSTR_LEN(domain)) {
187-
strlcat(cookie, COOKIE_DOMAIN, len + 100);
188-
strlcat(cookie, ZSTR_VAL(domain), len + 100);
171+
smart_str_appends(&buf, COOKIE_DOMAIN);
172+
smart_str_append(&buf, domain);
189173
}
190174
if (secure) {
191-
strlcat(cookie, COOKIE_SECURE, len + 100);
175+
smart_str_appends(&buf, COOKIE_SECURE);
192176
}
193177
if (httponly) {
194-
strlcat(cookie, COOKIE_HTTPONLY, len + 100);
178+
smart_str_appends(&buf, COOKIE_HTTPONLY);
195179
}
196180
if (samesite && ZSTR_LEN(samesite)) {
197-
strlcat(cookie, COOKIE_SAMESITE, len + 100);
198-
strlcat(cookie, ZSTR_VAL(samesite), len + 100);
181+
smart_str_appends(&buf, COOKIE_SAMESITE);
182+
smart_str_append(&buf, samesite);
199183
}
200184

201-
ctr.line = cookie;
202-
ctr.line_len = (uint32_t)strlen(cookie);
185+
ctr.line = ZSTR_VAL(buf.s);
186+
ctr.line_len = (uint32_t) ZSTR_LEN(buf.s);
203187

204188
result = sapi_header_op(SAPI_HEADER_ADD, &ctr);
205-
efree(cookie);
189+
zend_string_release(buf.s);
206190
return result;
207191
}
208192

0 commit comments

Comments
 (0)