udhcpd: clamp down huge auto_times to ~2M seconds, better EINTR poll handling
authorDenys Vlasenko <vda.linux@googlemail.com>
Sun, 11 Mar 2018 10:34:44 +0000 (11:34 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sun, 11 Mar 2018 22:04:05 +0000 (23:04 +0100)
EINTR _should_ only happen on two signals we trap, and safe_poll
_should_ work here just fine, but there were kernel bugs where spurious EINTRs
happen (e.g. on ptrace attach). Be safe.

function                                             old     new   delta
udhcpd_main                                         1437    1468     +31

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/udhcp/dhcpd.c

index f1368cc4d3bb95ce2c6e8abe9746862306c00da5..cfa994791edb059dcd26b8badab4b06a9a70a4ff 100644 (file)
@@ -853,6 +853,9 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
        /* Would rather not do read_config before daemonization -
         * otherwise NOMMU machines will parse config twice */
        read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
+       /* prevent poll timeout overflow */
+       if (server_config.auto_time > INT_MAX / 1000)
+               server_config.auto_time = INT_MAX / 1000;
 
        /* Make sure fd 0,1,2 are open */
        bb_sanitize_stdio();
@@ -914,14 +917,26 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
                }
 
                udhcp_sp_fd_set(pfds, server_socket);
-               tv = timeout_end - monotonic_sec();
-               /* Block here waiting for either signal or packet */
-               retval = safe_poll(pfds, 2, server_config.auto_time ? tv * 1000 : -1);
-               if (retval <= 0) {
-                       if (retval == 0) {
+
+ new_tv:
+               tv = -1;
+               if (server_config.auto_time) {
+                       tv = timeout_end - monotonic_sec();
+                       if (tv <= 0) {
+ write_leases:
                                write_leases();
                                goto continue_with_autotime;
                        }
+                       tv *= 1000;
+               }
+
+               /* Block here waiting for either signal or packet */
+               retval = poll(pfds, 2, tv);
+               if (retval <= 0) {
+                       if (retval == 0)
+                               goto write_leases;
+                       if (errno == EINTR)
+                               goto new_tv;
                        /* < 0 and not EINTR: should not happen */
                        bb_perror_msg_and_die("poll");
                }