kernel - Relax requirements for sysctl operations on longs master
authorMatthew Dillon <dillon@apollo.backplane.com>
Mon, 18 Dec 2017 05:45:34 +0000 (21:45 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Mon, 18 Dec 2017 05:45:34 +0000 (21:45 -0800)
* Relax the requirements for sysctl operations on longs.  Allow the
  case where userland is reading the sysctl variable as an integer
  instead of a long.

* Allows us to promote various vm.stats.vm_* fields that must now be
  longs in order to support > 8TB of ram as well as avoid internal
  calculation overflows.

* Fixes an issue where chrome issues this sysctl as an int:

  sysctlbyname("vm.stats.vm.v_page_count", &pages, &size, NULL, 0);

  And fails to check the return value / error code, resulting in
  a garbage value for 'pages' which chrome then uses to size the
  browser's memory restrains.  This in turn was causing chrome
  to give up multiple tabs thinking it had run out of memory when,
  in fact, there is plenty of memory available.

* Generally speaking, I've wanted to have this sort of backwards
  compatibility for a while now.  Userland shouldn't get errors
  accesing integral sysctl values simply because it uses an integer
  of a different size than the sysctl.

sys/kern/kern_sysctl.c

index 1606d5c..66f7dd6 100644 (file)
@@ -893,12 +893,28 @@ sysctl_handle_long(SYSCTL_HANDLER_ARGS)
 
        if (!arg1)
                return (EINVAL);
-       error = SYSCTL_OUT(req, arg1, sizeof(long));
+       if (req->oldlen == sizeof(int) &&
+           *(long *)arg1 >= INT_MIN &&
+           *(long *)arg1 <= INT_MAX) {
+               /*
+                * Backwards compatibility for read-only fields promoted
+                * from int to long.  Allow userland to request the field
+                * as an integer if the value is in-range.
+                */
+               int val = (int)*(long *)arg1;
+               error = SYSCTL_OUT(req, &val, sizeof(int));
+       } else {
+               /*
+                * Normal operation fo a long
+                */
+               error = SYSCTL_OUT(req, arg1, sizeof(long));
+       }
 
        if (error || !req->newptr)
                return (error);
 
        error = SYSCTL_IN(req, arg1, sizeof(long));
+
        return (error);
 }