Author: hselasky
Date: Wed Jun 20 20:04:20 2018
New Revision: 335461
URL: https://svnweb.freebsd.org/changeset/base/335461

Log:
  Permit the kernel environment to set an array of numeric values for a single
  sysctl(9) node.
  
  Reviewed by:          kib@, imp@, jhb@
  Differential Revision:        https://reviews.freebsd.org/D15802
  MFC after:            1 week
  Sponsored by:         Mellanox Technologies

Modified:
  head/sys/kern/kern_environment.c
  head/sys/kern/kern_sysctl.c
  head/sys/sys/systm.h

Modified: head/sys/kern/kern_environment.c
==============================================================================
--- head/sys/kern/kern_environment.c    Wed Jun 20 19:45:04 2018        
(r335460)
+++ head/sys/kern/kern_environment.c    Wed Jun 20 20:04:20 2018        
(r335461)
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/sysproto.h>
 #include <sys/libkern.h>
 #include <sys/kenv.h>
+#include <sys/limits.h>
 
 #include <security/mac/mac_framework.h>
 
@@ -514,6 +515,141 @@ getenv_string(const char *name, char *data, int size)
                        strlcpy(data, cp, size);
        }
        return (cp != NULL);
+}
+
+/*
+ * Return an array of integers at the given type size and signedness.
+ */
+int
+getenv_array(const char *name, void *pdata, int size, int *psize,
+    int type_size, bool allow_signed)
+{
+       char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1];
+       uint8_t shift;
+       int64_t value;
+       int64_t old;
+       char *end;
+       char *ptr;
+       int n;
+
+       if (getenv_string(name, buf, sizeof(buf)) == 0)
+               return (0);
+
+       /* get maximum number of elements */
+       size /= type_size;
+
+       n = 0;
+
+       for (ptr = buf; *ptr != 0; ) {
+
+               value = strtoq(ptr, &end, 0);
+
+               /* check if signed numbers are allowed */
+               if (value < 0 && !allow_signed)
+                       goto error;
+
+               /* check for invalid value */
+               if (ptr == end)
+                       goto error;
+               
+               /* check for valid suffix */
+               switch (*end) {
+               case 't':
+               case 'T':
+                       shift = 40;
+                       end++;
+                       break;
+               case 'g':
+               case 'G':
+                       shift = 30;
+                       end++;
+                       break;
+               case 'm':
+               case 'M':
+                       shift = 20;
+                       end++;
+                       break;
+               case 'k':
+               case 'K':
+                       shift = 10;
+                       end++;
+                       break;
+               case ' ':
+               case '\t':
+               case ',':
+               case 0:
+                       shift = 0;
+                       break;
+               default:
+                       /* garbage after numeric value */
+                       goto error;
+               }
+
+               /* skip till next value, if any */
+               while (*end == '\t' || *end == ',' || *end == ' ')
+                       end++;
+
+               /* update pointer */
+               ptr = end;
+
+               /* apply shift */
+               old = value;
+               value <<= shift;
+
+               /* overflow check */
+               if ((value >> shift) != old)
+                       goto error;
+
+               /* check for buffer overflow */
+               if (n >= size)
+                       goto error;
+
+               /* store value according to type size */
+               switch (type_size) {
+               case 1:
+                       if (allow_signed) {
+                               if (value < SCHAR_MIN || value > SCHAR_MAX)
+                                       goto error;
+                       } else {
+                               if (value < 0 || value > UCHAR_MAX)
+                                       goto error;
+                       }
+                       ((uint8_t *)pdata)[n] = (uint8_t)value;
+                       break;
+               case 2:
+                       if (allow_signed) {
+                               if (value < SHRT_MIN || value > SHRT_MAX)
+                                       goto error;
+                       } else {
+                               if (value < 0 || value > USHRT_MAX)
+                                       goto error;
+                       }
+                       ((uint16_t *)pdata)[n] = (uint16_t)value;
+                       break;
+               case 4:
+                       if (allow_signed) {
+                               if (value < INT_MIN || value > INT_MAX)
+                                       goto error;
+                       } else {
+                               if (value > UINT_MAX)
+                                       goto error;
+                       }
+                       ((uint32_t *)pdata)[n] = (uint32_t)value;
+                       break;
+               case 8:
+                       ((uint64_t *)pdata)[n] = (uint64_t)value;
+                       break;
+               default:
+                       goto error;
+               }
+               n++;
+       }
+       *psize = n * type_size;
+
+       if (n != 0)
+               return (1);     /* success */
+error:
+       return (0);     /* failure */
 }
 
 /*

Modified: head/sys/kern/kern_sysctl.c
==============================================================================
--- head/sys/kern/kern_sysctl.c Wed Jun 20 19:45:04 2018        (r335460)
+++ head/sys/kern/kern_sysctl.c Wed Jun 20 20:04:20 2018        (r335461)
@@ -192,13 +192,8 @@ sysctl_load_tunable_by_oid_locked(struct sysctl_oid *o
        char path[96];
        ssize_t rem = sizeof(path);
        ssize_t len;
-       uint8_t val_8;
-       uint16_t val_16;
-       uint32_t val_32;
-       int val_int;
-       long val_long;
-       int64_t val_64;
-       quad_t val_quad;
+       uint8_t data[512] __aligned(sizeof(uint64_t));
+       int size;
        int error;
 
        path[--rem] = 0;
@@ -226,85 +221,88 @@ sysctl_load_tunable_by_oid_locked(struct sysctl_oid *o
 
        switch (oidp->oid_kind & CTLTYPE) {
        case CTLTYPE_INT:
-               if (getenv_int(path + rem, &val_int) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(int), GETENV_SIGNED) == 0)
                        return;
-               req.newlen = sizeof(val_int);
-               req.newptr = &val_int;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_UINT:
-               if (getenv_uint(path + rem, (unsigned int *)&val_int) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(int), GETENV_UNSIGNED) == 0)
                        return;
-               req.newlen = sizeof(val_int);
-               req.newptr = &val_int;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_LONG:
-               if (getenv_long(path + rem, &val_long) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(long), GETENV_SIGNED) == 0)
                        return;
-               req.newlen = sizeof(val_long);
-               req.newptr = &val_long;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_ULONG:
-               if (getenv_ulong(path + rem, (unsigned long *)&val_long) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(long), GETENV_UNSIGNED) == 0)
                        return;
-               req.newlen = sizeof(val_long);
-               req.newptr = &val_long;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_S8:
-               if (getenv_int(path + rem, &val_int) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(int8_t), GETENV_SIGNED) == 0)
                        return;
-               val_8 = val_int;
-               req.newlen = sizeof(val_8);
-               req.newptr = &val_8;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_S16:
-               if (getenv_int(path + rem, &val_int) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(int16_t), GETENV_SIGNED) == 0)
                        return;
-               val_16 = val_int;
-               req.newlen = sizeof(val_16);
-               req.newptr = &val_16;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_S32:
-               if (getenv_long(path + rem, &val_long) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(int32_t), GETENV_SIGNED) == 0)
                        return;
-               val_32 = val_long;
-               req.newlen = sizeof(val_32);
-               req.newptr = &val_32;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_S64:
-               if (getenv_quad(path + rem, &val_quad) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(int64_t), GETENV_SIGNED) == 0)
                        return;
-               val_64 = val_quad;
-               req.newlen = sizeof(val_64);
-               req.newptr = &val_64;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_U8:
-               if (getenv_uint(path + rem, (unsigned int *)&val_int) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(uint8_t), GETENV_UNSIGNED) == 0)
                        return;
-               val_8 = val_int;
-               req.newlen = sizeof(val_8);
-               req.newptr = &val_8;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_U16:
-               if (getenv_uint(path + rem, (unsigned int *)&val_int) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(uint16_t), GETENV_UNSIGNED) == 0)
                        return;
-               val_16 = val_int;
-               req.newlen = sizeof(val_16);
-               req.newptr = &val_16;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_U32:
-               if (getenv_ulong(path + rem, (unsigned long *)&val_long) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(uint32_t), GETENV_UNSIGNED) == 0)
                        return;
-               val_32 = val_long;
-               req.newlen = sizeof(val_32);
-               req.newptr = &val_32;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_U64:
-               /* XXX there is no getenv_uquad() */
-               if (getenv_quad(path + rem, &val_quad) == 0)
+               if (getenv_array(path + rem, data, sizeof(data), &size,
+                   sizeof(uint64_t), GETENV_UNSIGNED) == 0)
                        return;
-               val_64 = val_quad;
-               req.newlen = sizeof(val_64);
-               req.newptr = &val_64;
+               req.newlen = size;
+               req.newptr = data;
                break;
        case CTLTYPE_STRING:
                penv = kern_getenv(path + rem);

Modified: head/sys/sys/systm.h
==============================================================================
--- head/sys/sys/systm.h        Wed Jun 20 19:45:04 2018        (r335460)
+++ head/sys/sys/systm.h        Wed Jun 20 20:04:20 2018        (r335461)
@@ -353,6 +353,11 @@ int        kern_setenv(const char *name, const char 
*value);
 int    kern_unsetenv(const char *name);
 int    testenv(const char *name);
 
+int    getenv_array(const char *name, void *data, int size, int *psize,
+    int type_size, bool allow_signed);
+#define        GETENV_UNSIGNED false   /* negative numbers not allowed */
+#define        GETENV_SIGNED   true    /* negative numbers allowed */
+
 typedef uint64_t (cpu_tick_f)(void);
 void set_cputicker(cpu_tick_f *func, uint64_t freq, unsigned var);
 extern cpu_tick_f *cpu_ticks;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to