aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJulian Anastasov <ja@ssi.bg>2014-09-02 17:02:49 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2014-09-09 10:31:24 -0400
commit5fcf0cf6073d4adb22e34cd1d14a6318699625a9 (patch)
treed07020081df5b3657c98b59fc8f0431f8d96f150 /net
parent3045d76070abe725dbb7fd8ff39c27b820d5a7eb (diff)
ipvs: reduce stack usage for sockopt data
Use union to reserve the required stack space for sockopt data which is less than the currently hardcoded value of 128. Now the tables for commands should be more readable. The checks added for readability are optimized by compiler, others warn at compile time if command uses too much stack or exceeds the storage of set_arglen and get_arglen. As Dan Carpenter points out, we can run for unprivileged user, so we can silent some error messages. Signed-off-by: Julian Anastasov <ja@ssi.bg> CC: Dan Carpenter <dan.carpenter@oracle.com> CC: Andrey Utkin <andrey.krieger.utkin@gmail.com> CC: David Binderman <dcb314@hotmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c111
1 files changed, 61 insertions, 50 deletions
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index fd3f444a4f96..bd2b208ba56c 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2179,29 +2179,41 @@ static int ip_vs_set_timeout(struct net *net, struct ip_vs_timeout_user *u)
2179 return 0; 2179 return 0;
2180} 2180}
2181 2181
2182#define CMDID(cmd) (cmd - IP_VS_BASE_CTL)
2182 2183
2183#define SET_CMDID(cmd) (cmd - IP_VS_BASE_CTL) 2184struct ip_vs_svcdest_user {
2184#define SERVICE_ARG_LEN (sizeof(struct ip_vs_service_user)) 2185 struct ip_vs_service_user s;
2185#define SVCDEST_ARG_LEN (sizeof(struct ip_vs_service_user) + \ 2186 struct ip_vs_dest_user d;
2186 sizeof(struct ip_vs_dest_user))
2187#define TIMEOUT_ARG_LEN (sizeof(struct ip_vs_timeout_user))
2188#define DAEMON_ARG_LEN (sizeof(struct ip_vs_daemon_user))
2189#define MAX_ARG_LEN SVCDEST_ARG_LEN
2190
2191static const unsigned char set_arglen[SET_CMDID(IP_VS_SO_SET_MAX)+1] = {
2192 [SET_CMDID(IP_VS_SO_SET_ADD)] = SERVICE_ARG_LEN,
2193 [SET_CMDID(IP_VS_SO_SET_EDIT)] = SERVICE_ARG_LEN,
2194 [SET_CMDID(IP_VS_SO_SET_DEL)] = SERVICE_ARG_LEN,
2195 [SET_CMDID(IP_VS_SO_SET_FLUSH)] = 0,
2196 [SET_CMDID(IP_VS_SO_SET_ADDDEST)] = SVCDEST_ARG_LEN,
2197 [SET_CMDID(IP_VS_SO_SET_DELDEST)] = SVCDEST_ARG_LEN,
2198 [SET_CMDID(IP_VS_SO_SET_EDITDEST)] = SVCDEST_ARG_LEN,
2199 [SET_CMDID(IP_VS_SO_SET_TIMEOUT)] = TIMEOUT_ARG_LEN,
2200 [SET_CMDID(IP_VS_SO_SET_STARTDAEMON)] = DAEMON_ARG_LEN,
2201 [SET_CMDID(IP_VS_SO_SET_STOPDAEMON)] = DAEMON_ARG_LEN,
2202 [SET_CMDID(IP_VS_SO_SET_ZERO)] = SERVICE_ARG_LEN,
2203}; 2187};
2204 2188
2189static const unsigned char set_arglen[CMDID(IP_VS_SO_SET_MAX) + 1] = {
2190 [CMDID(IP_VS_SO_SET_ADD)] = sizeof(struct ip_vs_service_user),
2191 [CMDID(IP_VS_SO_SET_EDIT)] = sizeof(struct ip_vs_service_user),
2192 [CMDID(IP_VS_SO_SET_DEL)] = sizeof(struct ip_vs_service_user),
2193 [CMDID(IP_VS_SO_SET_ADDDEST)] = sizeof(struct ip_vs_svcdest_user),
2194 [CMDID(IP_VS_SO_SET_DELDEST)] = sizeof(struct ip_vs_svcdest_user),
2195 [CMDID(IP_VS_SO_SET_EDITDEST)] = sizeof(struct ip_vs_svcdest_user),
2196 [CMDID(IP_VS_SO_SET_TIMEOUT)] = sizeof(struct ip_vs_timeout_user),
2197 [CMDID(IP_VS_SO_SET_STARTDAEMON)] = sizeof(struct ip_vs_daemon_user),
2198 [CMDID(IP_VS_SO_SET_STOPDAEMON)] = sizeof(struct ip_vs_daemon_user),
2199 [CMDID(IP_VS_SO_SET_ZERO)] = sizeof(struct ip_vs_service_user),
2200};
2201
2202union ip_vs_set_arglen {
2203 struct ip_vs_service_user field_IP_VS_SO_SET_ADD;
2204 struct ip_vs_service_user field_IP_VS_SO_SET_EDIT;
2205 struct ip_vs_service_user field_IP_VS_SO_SET_DEL;
2206 struct ip_vs_svcdest_user field_IP_VS_SO_SET_ADDDEST;
2207 struct ip_vs_svcdest_user field_IP_VS_SO_SET_DELDEST;
2208 struct ip_vs_svcdest_user field_IP_VS_SO_SET_EDITDEST;
2209 struct ip_vs_timeout_user field_IP_VS_SO_SET_TIMEOUT;
2210 struct ip_vs_daemon_user field_IP_VS_SO_SET_STARTDAEMON;
2211 struct ip_vs_daemon_user field_IP_VS_SO_SET_STOPDAEMON;
2212 struct ip_vs_service_user field_IP_VS_SO_SET_ZERO;
2213};
2214
2215#define MAX_SET_ARGLEN sizeof(union ip_vs_set_arglen)
2216
2205static void ip_vs_copy_usvc_compat(struct ip_vs_service_user_kern *usvc, 2217static void ip_vs_copy_usvc_compat(struct ip_vs_service_user_kern *usvc,
2206 struct ip_vs_service_user *usvc_compat) 2218 struct ip_vs_service_user *usvc_compat)
2207{ 2219{
@@ -2239,7 +2251,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2239{ 2251{
2240 struct net *net = sock_net(sk); 2252 struct net *net = sock_net(sk);
2241 int ret; 2253 int ret;
2242 unsigned char arg[MAX_ARG_LEN]; 2254 unsigned char arg[MAX_SET_ARGLEN];
2243 struct ip_vs_service_user *usvc_compat; 2255 struct ip_vs_service_user *usvc_compat;
2244 struct ip_vs_service_user_kern usvc; 2256 struct ip_vs_service_user_kern usvc;
2245 struct ip_vs_service *svc; 2257 struct ip_vs_service *svc;
@@ -2247,16 +2259,15 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2247 struct ip_vs_dest_user_kern udest; 2259 struct ip_vs_dest_user_kern udest;
2248 struct netns_ipvs *ipvs = net_ipvs(net); 2260 struct netns_ipvs *ipvs = net_ipvs(net);
2249 2261
2262 BUILD_BUG_ON(sizeof(arg) > 255);
2250 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) 2263 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
2251 return -EPERM; 2264 return -EPERM;
2252 2265
2253 if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_SET_MAX) 2266 if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_SET_MAX)
2254 return -EINVAL; 2267 return -EINVAL;
2255 if (len < 0 || len > MAX_ARG_LEN) 2268 if (len != set_arglen[CMDID(cmd)]) {
2256 return -EINVAL; 2269 IP_VS_DBG(1, "set_ctl: len %u != %u\n",
2257 if (len != set_arglen[SET_CMDID(cmd)]) { 2270 len, set_arglen[CMDID(cmd)]);
2258 pr_err("set_ctl: len %u != %u\n",
2259 len, set_arglen[SET_CMDID(cmd)]);
2260 return -EINVAL; 2271 return -EINVAL;
2261 } 2272 }
2262 2273
@@ -2512,51 +2523,51 @@ __ip_vs_get_timeouts(struct net *net, struct ip_vs_timeout_user *u)
2512#endif 2523#endif
2513} 2524}
2514 2525
2526static const unsigned char get_arglen[CMDID(IP_VS_SO_GET_MAX) + 1] = {
2527 [CMDID(IP_VS_SO_GET_VERSION)] = 64,
2528 [CMDID(IP_VS_SO_GET_INFO)] = sizeof(struct ip_vs_getinfo),
2529 [CMDID(IP_VS_SO_GET_SERVICES)] = sizeof(struct ip_vs_get_services),
2530 [CMDID(IP_VS_SO_GET_SERVICE)] = sizeof(struct ip_vs_service_entry),
2531 [CMDID(IP_VS_SO_GET_DESTS)] = sizeof(struct ip_vs_get_dests),
2532 [CMDID(IP_VS_SO_GET_TIMEOUT)] = sizeof(struct ip_vs_timeout_user),
2533 [CMDID(IP_VS_SO_GET_DAEMON)] = 2 * sizeof(struct ip_vs_daemon_user),
2534};
2515 2535
2516#define GET_CMDID(cmd) (cmd - IP_VS_BASE_CTL) 2536union ip_vs_get_arglen {
2517#define GET_INFO_ARG_LEN (sizeof(struct ip_vs_getinfo)) 2537 char field_IP_VS_SO_GET_VERSION[64];
2518#define GET_SERVICES_ARG_LEN (sizeof(struct ip_vs_get_services)) 2538 struct ip_vs_getinfo field_IP_VS_SO_GET_INFO;
2519#define GET_SERVICE_ARG_LEN (sizeof(struct ip_vs_service_entry)) 2539 struct ip_vs_get_services field_IP_VS_SO_GET_SERVICES;
2520#define GET_DESTS_ARG_LEN (sizeof(struct ip_vs_get_dests)) 2540 struct ip_vs_service_entry field_IP_VS_SO_GET_SERVICE;
2521#define GET_TIMEOUT_ARG_LEN (sizeof(struct ip_vs_timeout_user)) 2541 struct ip_vs_get_dests field_IP_VS_SO_GET_DESTS;
2522#define GET_DAEMON_ARG_LEN (sizeof(struct ip_vs_daemon_user) * 2) 2542 struct ip_vs_timeout_user field_IP_VS_SO_GET_TIMEOUT;
2523 2543 struct ip_vs_daemon_user field_IP_VS_SO_GET_DAEMON[2];
2524static const unsigned char get_arglen[GET_CMDID(IP_VS_SO_GET_MAX)+1] = {
2525 [GET_CMDID(IP_VS_SO_GET_VERSION)] = 64,
2526 [GET_CMDID(IP_VS_SO_GET_INFO)] = GET_INFO_ARG_LEN,
2527 [GET_CMDID(IP_VS_SO_GET_SERVICES)] = GET_SERVICES_ARG_LEN,
2528 [GET_CMDID(IP_VS_SO_GET_SERVICE)] = GET_SERVICE_ARG_LEN,
2529 [GET_CMDID(IP_VS_SO_GET_DESTS)] = GET_DESTS_ARG_LEN,
2530 [GET_CMDID(IP_VS_SO_GET_TIMEOUT)] = GET_TIMEOUT_ARG_LEN,
2531 [GET_CMDID(IP_VS_SO_GET_DAEMON)] = GET_DAEMON_ARG_LEN,
2532}; 2544};
2533 2545
2546#define MAX_GET_ARGLEN sizeof(union ip_vs_get_arglen)
2547
2534static int 2548static int
2535do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) 2549do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2536{ 2550{
2537 unsigned char arg[128]; 2551 unsigned char arg[MAX_GET_ARGLEN];
2538 int ret = 0; 2552 int ret = 0;
2539 unsigned int copylen; 2553 unsigned int copylen;
2540 struct net *net = sock_net(sk); 2554 struct net *net = sock_net(sk);
2541 struct netns_ipvs *ipvs = net_ipvs(net); 2555 struct netns_ipvs *ipvs = net_ipvs(net);
2542 2556
2543 BUG_ON(!net); 2557 BUG_ON(!net);
2558 BUILD_BUG_ON(sizeof(arg) > 255);
2544 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) 2559 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
2545 return -EPERM; 2560 return -EPERM;
2546 2561
2547 if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_GET_MAX) 2562 if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_GET_MAX)
2548 return -EINVAL; 2563 return -EINVAL;
2549 2564
2550 if (*len < get_arglen[GET_CMDID(cmd)]) { 2565 copylen = get_arglen[CMDID(cmd)];
2551 pr_err("get_ctl: len %u < %u\n", 2566 if (*len < (int) copylen) {
2552 *len, get_arglen[GET_CMDID(cmd)]); 2567 IP_VS_DBG(1, "get_ctl: len %d < %u\n", *len, copylen);
2553 return -EINVAL; 2568 return -EINVAL;
2554 } 2569 }
2555 2570
2556 copylen = get_arglen[GET_CMDID(cmd)];
2557 if (copylen > 128)
2558 return -EINVAL;
2559
2560 if (copy_from_user(arg, user, copylen) != 0) 2571 if (copy_from_user(arg, user, copylen) != 0)
2561 return -EFAULT; 2572 return -EFAULT;
2562 /* 2573 /*