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
This commit is contained in:
parent
80e7201f85
commit
86f50c1a6b
@ -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>
|
||||
|
||||
@ -516,6 +517,141 @@ getenv_string(const char *name, char *data, int 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 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an integer value from an environment variable.
|
||||
*/
|
||||
|
@ -192,13 +192,8 @@ sysctl_load_tunable_by_oid_locked(struct sysctl_oid *oidp)
|
||||
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 *oidp)
|
||||
|
||||
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);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user