Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Julian Anastasov and committed by
Pablo Neira Ayuso
5fcf0cf6 3045d760

+62 -51
+62 -51
net/netfilter/ipvs/ip_vs_ctl.c
··· 2179 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) 2184 - #define SERVICE_ARG_LEN (sizeof(struct ip_vs_service_user)) 2185 - #define SVCDEST_ARG_LEN (sizeof(struct ip_vs_service_user) + \ 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 - 2191 - static 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, 2184 + struct ip_vs_svcdest_user { 2185 + struct ip_vs_service_user s; 2186 + struct ip_vs_dest_user d; 2203 2187 }; 2188 + 2189 + static 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 + 2202 + union 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) 2204 2216 2205 2217 static void ip_vs_copy_usvc_compat(struct ip_vs_service_user_kern *usvc, 2206 2218 struct ip_vs_service_user *usvc_compat) ··· 2251 2239 { 2252 2240 struct net *net = sock_net(sk); 2253 2241 int ret; 2254 - unsigned char arg[MAX_ARG_LEN]; 2242 + unsigned char arg[MAX_SET_ARGLEN]; 2255 2243 struct ip_vs_service_user *usvc_compat; 2256 2244 struct ip_vs_service_user_kern usvc; 2257 2245 struct ip_vs_service *svc; ··· 2259 2247 struct ip_vs_dest_user_kern udest; 2260 2248 struct netns_ipvs *ipvs = net_ipvs(net); 2261 2249 2250 + BUILD_BUG_ON(sizeof(arg) > 255); 2262 2251 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) 2263 2252 return -EPERM; 2264 2253 2265 2254 if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_SET_MAX) 2266 2255 return -EINVAL; 2267 - if (len < 0 || len > MAX_ARG_LEN) 2268 - return -EINVAL; 2269 - if (len != set_arglen[SET_CMDID(cmd)]) { 2270 - pr_err("set_ctl: len %u != %u\n", 2271 - len, set_arglen[SET_CMDID(cmd)]); 2256 + if (len != set_arglen[CMDID(cmd)]) { 2257 + IP_VS_DBG(1, "set_ctl: len %u != %u\n", 2258 + len, set_arglen[CMDID(cmd)]); 2272 2259 return -EINVAL; 2273 2260 } 2274 2261 ··· 2523 2512 #endif 2524 2513 } 2525 2514 2526 - 2527 - #define GET_CMDID(cmd) (cmd - IP_VS_BASE_CTL) 2528 - #define GET_INFO_ARG_LEN (sizeof(struct ip_vs_getinfo)) 2529 - #define GET_SERVICES_ARG_LEN (sizeof(struct ip_vs_get_services)) 2530 - #define GET_SERVICE_ARG_LEN (sizeof(struct ip_vs_service_entry)) 2531 - #define GET_DESTS_ARG_LEN (sizeof(struct ip_vs_get_dests)) 2532 - #define GET_TIMEOUT_ARG_LEN (sizeof(struct ip_vs_timeout_user)) 2533 - #define GET_DAEMON_ARG_LEN (sizeof(struct ip_vs_daemon_user) * 2) 2534 - 2535 - static const unsigned char get_arglen[GET_CMDID(IP_VS_SO_GET_MAX)+1] = { 2536 - [GET_CMDID(IP_VS_SO_GET_VERSION)] = 64, 2537 - [GET_CMDID(IP_VS_SO_GET_INFO)] = GET_INFO_ARG_LEN, 2538 - [GET_CMDID(IP_VS_SO_GET_SERVICES)] = GET_SERVICES_ARG_LEN, 2539 - [GET_CMDID(IP_VS_SO_GET_SERVICE)] = GET_SERVICE_ARG_LEN, 2540 - [GET_CMDID(IP_VS_SO_GET_DESTS)] = GET_DESTS_ARG_LEN, 2541 - [GET_CMDID(IP_VS_SO_GET_TIMEOUT)] = GET_TIMEOUT_ARG_LEN, 2542 - [GET_CMDID(IP_VS_SO_GET_DAEMON)] = GET_DAEMON_ARG_LEN, 2515 + static const unsigned char get_arglen[CMDID(IP_VS_SO_GET_MAX) + 1] = { 2516 + [CMDID(IP_VS_SO_GET_VERSION)] = 64, 2517 + [CMDID(IP_VS_SO_GET_INFO)] = sizeof(struct ip_vs_getinfo), 2518 + [CMDID(IP_VS_SO_GET_SERVICES)] = sizeof(struct ip_vs_get_services), 2519 + [CMDID(IP_VS_SO_GET_SERVICE)] = sizeof(struct ip_vs_service_entry), 2520 + [CMDID(IP_VS_SO_GET_DESTS)] = sizeof(struct ip_vs_get_dests), 2521 + [CMDID(IP_VS_SO_GET_TIMEOUT)] = sizeof(struct ip_vs_timeout_user), 2522 + [CMDID(IP_VS_SO_GET_DAEMON)] = 2 * sizeof(struct ip_vs_daemon_user), 2543 2523 }; 2524 + 2525 + union ip_vs_get_arglen { 2526 + char field_IP_VS_SO_GET_VERSION[64]; 2527 + struct ip_vs_getinfo field_IP_VS_SO_GET_INFO; 2528 + struct ip_vs_get_services field_IP_VS_SO_GET_SERVICES; 2529 + struct ip_vs_service_entry field_IP_VS_SO_GET_SERVICE; 2530 + struct ip_vs_get_dests field_IP_VS_SO_GET_DESTS; 2531 + struct ip_vs_timeout_user field_IP_VS_SO_GET_TIMEOUT; 2532 + struct ip_vs_daemon_user field_IP_VS_SO_GET_DAEMON[2]; 2533 + }; 2534 + 2535 + #define MAX_GET_ARGLEN sizeof(union ip_vs_get_arglen) 2544 2536 2545 2537 static int 2546 2538 do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) 2547 2539 { 2548 - unsigned char arg[128]; 2540 + unsigned char arg[MAX_GET_ARGLEN]; 2549 2541 int ret = 0; 2550 2542 unsigned int copylen; 2551 2543 struct net *net = sock_net(sk); 2552 2544 struct netns_ipvs *ipvs = net_ipvs(net); 2553 2545 2554 2546 BUG_ON(!net); 2547 + BUILD_BUG_ON(sizeof(arg) > 255); 2555 2548 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) 2556 2549 return -EPERM; 2557 2550 2558 2551 if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_GET_MAX) 2559 2552 return -EINVAL; 2560 2553 2561 - if (*len < get_arglen[GET_CMDID(cmd)]) { 2562 - pr_err("get_ctl: len %u < %u\n", 2563 - *len, get_arglen[GET_CMDID(cmd)]); 2554 + copylen = get_arglen[CMDID(cmd)]; 2555 + if (*len < (int) copylen) { 2556 + IP_VS_DBG(1, "get_ctl: len %d < %u\n", *len, copylen); 2564 2557 return -EINVAL; 2565 2558 } 2566 - 2567 - copylen = get_arglen[GET_CMDID(cmd)]; 2568 - if (copylen > 128) 2569 - return -EINVAL; 2570 2559 2571 2560 if (copy_from_user(arg, user, copylen) != 0) 2572 2561 return -EFAULT;