Skip to content

Commit 8a649a8

Browse files
committed
ext/sockets: socket_set_option switch from convert_to_long to zval_get_long.
to be explicit when the expected type is not met. Check SO_LINGER values for possible overflow. close GH-17135
1 parent 6c198e3 commit 8a649a8

File tree

4 files changed

+123
-19
lines changed

4 files changed

+123
-19
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ PHP NEWS
4848
- Sockets:
4949
. Fixed bug GH-16276 (socket_strerror overflow handling with INT_MIN).
5050
(David Carlier / cmb)
51+
. Fixed overflow on SO_LINGER values setting, strengthening values check
52+
on SO_SNDTIMEO/SO_RCVTIMEO for socket_set_option().
53+
(David Carlier)
5154

5255
- Streams:
5356
. Fixed bug GH-17037 (UAF in user filter when adding existing filter name due

ext/sockets/sockets.c

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,8 +1871,16 @@ PHP_FUNCTION(socket_set_option)
18711871
const char l_onoff_key[] = "l_onoff";
18721872
const char l_linger_key[] = "l_linger";
18731873

1874-
convert_to_array(arg4);
1875-
opt_ht = Z_ARRVAL_P(arg4);
1874+
if (Z_TYPE_P(arg4) != IS_ARRAY) {
1875+
if (UNEXPECTED(Z_TYPE_P(arg4) != IS_OBJECT)) {
1876+
zend_argument_type_error(4, "must be of type array when argument #3 ($option) is SO_LINGER, %s given", zend_zval_value_name(arg4));
1877+
RETURN_THROWS();
1878+
} else {
1879+
opt_ht = Z_OBJPROP_P(arg4);
1880+
}
1881+
} else {
1882+
opt_ht = Z_ARRVAL_P(arg4);
1883+
}
18761884

18771885
if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == NULL) {
18781886
zend_argument_value_error(4, "must have key \"%s\"", l_onoff_key);
@@ -1883,11 +1891,21 @@ PHP_FUNCTION(socket_set_option)
18831891
RETURN_THROWS();
18841892
}
18851893

1886-
convert_to_long(l_onoff);
1887-
convert_to_long(l_linger);
1894+
zend_long val_lonoff = zval_get_long(l_onoff);
1895+
zend_long val_linger = zval_get_long(l_linger);
1896+
1897+
if (val_lonoff < 0 || val_lonoff > USHRT_MAX) {
1898+
zend_argument_value_error(4, "\"%s\" must be between 0 and %u", l_onoff_key, USHRT_MAX);
1899+
RETURN_THROWS();
1900+
}
1901+
1902+
if (val_linger < 0 || val_linger > USHRT_MAX) {
1903+
zend_argument_value_error(4, "\"%s\" must be between 0 and %d", l_linger, USHRT_MAX);
1904+
RETURN_THROWS();
1905+
}
18881906

1889-
lv.l_onoff = (unsigned short)Z_LVAL_P(l_onoff);
1890-
lv.l_linger = (unsigned short)Z_LVAL_P(l_linger);
1907+
lv.l_onoff = (unsigned short)val_lonoff;
1908+
lv.l_linger = (unsigned short)val_linger;
18911909

18921910
optlen = sizeof(lv);
18931911
opt_ptr = &lv;
@@ -1899,8 +1917,18 @@ PHP_FUNCTION(socket_set_option)
18991917
const char sec_key[] = "sec";
19001918
const char usec_key[] = "usec";
19011919

1902-
convert_to_array(arg4);
1903-
opt_ht = Z_ARRVAL_P(arg4);
1920+
if (Z_TYPE_P(arg4) != IS_ARRAY) {
1921+
if (UNEXPECTED(Z_TYPE_P(arg4) != IS_OBJECT)) {
1922+
zend_argument_type_error(4, "must be of type array when argument #3 ($option) is %s, %s given",
1923+
optname == SO_RCVTIMEO ? "SO_RCVTIMEO" : "SO_SNDTIMEO",
1924+
zend_zval_value_name(arg4));
1925+
RETURN_THROWS();
1926+
} else {
1927+
opt_ht = Z_OBJPROP_P(arg4);
1928+
}
1929+
} else {
1930+
opt_ht = Z_ARRVAL_P(arg4);
1931+
}
19041932

19051933
if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) {
19061934
zend_argument_value_error(4, "must have key \"%s\"", sec_key);
@@ -1911,15 +1939,16 @@ PHP_FUNCTION(socket_set_option)
19111939
RETURN_THROWS();
19121940
}
19131941

1914-
convert_to_long(sec);
1915-
convert_to_long(usec);
1942+
zend_long valsec = zval_get_long(sec);
1943+
zend_long valusec = zval_get_long(usec);
19161944
#ifndef PHP_WIN32
1917-
tv.tv_sec = Z_LVAL_P(sec);
1918-
tv.tv_usec = Z_LVAL_P(usec);
1945+
tv.tv_sec = valsec;
1946+
tv.tv_usec = valusec;
19191947
optlen = sizeof(tv);
19201948
opt_ptr = &tv;
19211949
#else
1922-
timeout = Z_LVAL_P(sec) * 1000 + Z_LVAL_P(usec) / 1000;
1950+
timeout = valsec * 1000 + valusec / 1000;
1951+
19231952
optlen = sizeof(int);
19241953
opt_ptr = &timeout;
19251954
#endif
@@ -1971,15 +2000,15 @@ PHP_FUNCTION(socket_set_option)
19712000

19722001
#ifdef SO_ATTACH_REUSEPORT_CBPF
19732002
case SO_ATTACH_REUSEPORT_CBPF: {
1974-
convert_to_long(arg4);
2003+
zend_long cbpf_val = zval_get_long(arg4);
19752004

1976-
if (!Z_LVAL_P(arg4)) {
2005+
if (!cbpf_val) {
19772006
ov = 1;
19782007
optlen = sizeof(ov);
19792008
opt_ptr = &ov;
19802009
optname = SO_DETACH_BPF;
19812010
} else {
1982-
uint32_t k = (uint32_t)Z_LVAL_P(arg4);
2011+
uint32_t k = (uint32_t)cbpf_val;
19832012
static struct sock_filter cbpf[8] = {0};
19842013
static struct sock_fprog bpfprog;
19852014

@@ -2006,8 +2035,7 @@ PHP_FUNCTION(socket_set_option)
20062035

20072036
default:
20082037
default_case:
2009-
convert_to_long(arg4);
2010-
ov = Z_LVAL_P(arg4);
2038+
ov = zval_get_long(arg4);
20112039

20122040
optlen = sizeof(ov);
20132041
opt_ptr = &ov;

ext/sockets/tests/socket_reuseport_cbpf.phpt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,20 @@ if (!$socket) {
1818
}
1919
var_dump(socket_set_option( $socket, SOL_SOCKET, SO_REUSEADDR, true));
2020
var_dump(socket_set_option( $socket, SOL_SOCKET, SO_REUSEPORT, true));
21+
try {
22+
socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, array());
23+
} catch (\TypeError $e) {
24+
echo $e->getMessage() . PHP_EOL;
25+
}
2126
var_dump(socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SKF_AD_CPU));
2227
var_dump(socket_bind($socket, '0.0.0.0'));
2328
socket_listen($socket);
2429
socket_close($socket);
2530
?>
26-
--EXPECT--
31+
--EXPECTF--
2732
bool(true)
2833
bool(true)
34+
35+
Warning: socket_set_option(): Unable to set socket option [2]: No such file or directory in %s on line %d
2936
bool(true)
3037
bool(true)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
--TEST--
2+
socket_set_option() with SO_RCVTIMEO/SO_SNDTIMEO/SO_LINGER
3+
--EXTENSIONS--
4+
sockets
5+
--FILE--
6+
<?php
7+
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
8+
if (!$socket) {
9+
die('Unable to create AF_INET socket [socket]');
10+
}
11+
$options_1 = array("sec" => 1, "usec" => "aaaaa");
12+
$options_2 = array("sec" => new stdClass(), "usec" => "1");
13+
$options_3 = array("l_onoff" => "aaaa", "l_linger" => "1");
14+
$options_4 = array("l_onoff" => "1", "l_linger" => []);
15+
$options_5 = array("l_onoff" => PHP_INT_MAX, "l_linger" => "1");
16+
17+
try {
18+
socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, new stdClass);
19+
} catch (\ValueError $e) {
20+
echo $e->getMessage() . PHP_EOL;
21+
}
22+
23+
try {
24+
socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, $options_1);
25+
} catch (\TypeError $e) {
26+
echo $e->getMessage() . PHP_EOL;
27+
}
28+
29+
try {
30+
socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, $options_2);
31+
} catch (\TypeError $e) {
32+
echo $e->getMessage() . PHP_EOL;
33+
}
34+
try {
35+
socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, "not good");
36+
} catch (\TypeError $e) {
37+
echo $e->getMessage() . PHP_EOL;
38+
}
39+
try {
40+
socket_set_option( $socket, SOL_SOCKET, SO_LINGER, "not good neither");
41+
} catch (\TypeError $e) {
42+
echo $e->getMessage() . PHP_EOL;
43+
}
44+
try {
45+
socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_3);
46+
} catch (\TypeError $e) {
47+
echo $e->getMessage() . PHP_EOL;
48+
}
49+
try {
50+
socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_4);
51+
} catch (\TypeError $e) {
52+
echo $e->getMessage() . PHP_EOL;
53+
}
54+
try {
55+
socket_set_option( $socket, SOL_SOCKET, SO_LINGER, $options_5);
56+
} catch (\ValueError $e) {
57+
echo $e->getMessage() . PHP_EOL;
58+
}
59+
?>
60+
--EXPECTF--
61+
socket_set_option(): Argument #4 ($value) must have key "sec"
62+
63+
Warning: Object of class stdClass could not be converted to int in %s on line %d
64+
socket_set_option(): Argument #4 ($value) must be of type array when argument #3 ($option) is SO_RCVTIMEO, string given
65+
socket_set_option(): Argument #4 ($value) must be of type array when argument #3 ($option) is SO_LINGER, string given
66+
socket_set_option(): Argument #4 ($value) "l_onoff" must be between 0 and %d

0 commit comments

Comments
 (0)