Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 20 Jun 2018 20:04:20 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r335461 - in head/sys: kern sys
Message-ID:  <201806202004.w5KK4KN2077781@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201806202004.w5KK4KN2077781>