Make KENV_MVALLEN tunable

When doing secure boot, loader wants to export loader.ve.hashed
the value of which typically exceeds KENV_MVALLEN.

Replace use of KENV_MVALLEN with tunable kenv_mvallen.

Add getenv_string_buffer() for the case where a stack buffer cannot be
created and use uma_zone_t kenv_zone for suitably sized buffers.

Reviewed by:	stevek, kevans
Obtained from:	Abhishek Kulkarni <abkulkarni@juniper.net>
MFC after:	1 week
Sponsored by:	Juniper Networks
Differential Revision: https://reviews.freebsd.org//D25259
This commit is contained in:
Simon J. Gerraty 2020-06-16 17:02:56 +00:00
parent 421f325efc
commit 73845fdbd3
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=362231

View File

@ -63,6 +63,9 @@ static MALLOC_DEFINE(M_KENV, "kenv", "kernel environment");
#define KENV_SIZE 512 /* Maximum number of environment strings */
static uma_zone_t kenv_zone;
static int kenv_mvallen = KENV_MVALLEN;
/* pointer to the config-generated static environment */
char *kern_envp;
@ -85,6 +88,8 @@ bool dynamic_kenv;
#define KENV_CHECK if (!dynamic_kenv) \
panic("%s: called before SI_SUB_KMEM", __func__)
static char *getenv_string_buffer(const char *);
int
sys_kenv(td, uap)
struct thread *td;
@ -110,9 +115,9 @@ sys_kenv(td, uap)
#endif
done = needed = 0;
buflen = uap->len;
if (buflen > KENV_SIZE * (KENV_MNAMELEN + KENV_MVALLEN + 2))
if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2))
buflen = KENV_SIZE * (KENV_MNAMELEN +
KENV_MVALLEN + 2);
kenv_mvallen + 2);
if (uap->len > 0 && uap->value != NULL)
buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
mtx_lock(&kenv_lock);
@ -185,8 +190,8 @@ sys_kenv(td, uap)
error = EINVAL;
goto done;
}
if (len > KENV_MVALLEN + 1)
len = KENV_MVALLEN + 1;
if (len > kenv_mvallen + 1)
len = kenv_mvallen + 1;
value = malloc(len, M_TEMP, M_WAITOK);
error = copyinstr(uap->value, value, len, NULL);
if (error) {
@ -327,7 +332,7 @@ init_dynamic_kenv_from(char *init_env, int *curpos)
for (cp = init_env; cp != NULL; cp = cpnext) {
cpnext = kernenv_next(cp);
len = strlen(cp) + 1;
if (len > KENV_MNAMELEN + 1 + KENV_MVALLEN + 1) {
if (len > KENV_MNAMELEN + 1 + kenv_mvallen + 1) {
printf(
"WARNING: too long kenv string, ignoring %s\n",
cp);
@ -375,6 +380,13 @@ static void
init_dynamic_kenv(void *data __unused)
{
int dynamic_envpos;
int size;
TUNABLE_INT_FETCH("kenv_mvallen", &kenv_mvallen);
size = KENV_MNAMELEN + 1 + kenv_mvallen + 1;
kenv_zone = uma_zcreate("kenv", size, NULL, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
kenvp = malloc((KENV_SIZE + 1) * sizeof(char *), M_KENV,
M_WAITOK | M_ZERO);
@ -395,7 +407,7 @@ freeenv(char *env)
if (dynamic_kenv && env != NULL) {
explicit_bzero(env, strlen(env));
free(env, M_KENV);
uma_zfree(kenv_zone, env);
}
}
@ -470,14 +482,11 @@ _getenv_static(const char *name)
char *
kern_getenv(const char *name)
{
char buf[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1];
char *ret;
if (dynamic_kenv) {
if (getenv_string(name, buf, sizeof(buf))) {
ret = strdup(buf, M_KENV);
} else {
ret = NULL;
ret = getenv_string_buffer(name);
if (ret == NULL) {
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"getenv");
}
@ -548,7 +557,7 @@ kern_setenv(const char *name, const char *value)
if (namelen > KENV_MNAMELEN + 1)
return (-1);
vallen = strlen(value) + 1;
if (vallen > KENV_MVALLEN + 1)
if (vallen > kenv_mvallen + 1)
return (-1);
buf = malloc(namelen + vallen, M_KENV, M_WAITOK);
sprintf(buf, "%s=%s", name, value);
@ -606,6 +615,33 @@ kern_unsetenv(const char *name)
return (-1);
}
/*
* Return a buffer containing the string value from an environment variable
*/
static char *
getenv_string_buffer(const char *name)
{
char *cp, *ret;
int len;
if (dynamic_kenv) {
len = KENV_MNAMELEN + 1 + kenv_mvallen + 1;
ret = uma_zalloc(kenv_zone, M_WAITOK | M_ZERO);
mtx_lock(&kenv_lock);
cp = _getenv_dynamic(name, NULL);
if (cp != NULL)
strlcpy(ret, cp, len);
mtx_unlock(&kenv_lock);
if (cp == NULL) {
uma_zfree(kenv_zone, ret);
ret = NULL;
}
} else
ret = _getenv_static(name);
return (ret);
}
/*
* Return a string value from an environment variable.
*/
@ -635,17 +671,19 @@ 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 *buf;
char *end;
char *ptr;
int n;
int rc;
if (getenv_string(name, buf, sizeof(buf)) == 0)
if ((buf = getenv_string_buffer(name)) == NULL)
return (0);
rc = 0; /* assume failure */
/* get maximum number of elements */
size /= type_size;
@ -758,9 +796,11 @@ getenv_array(const char *name, void *pdata, int size, int *psize,
*psize = n * type_size;
if (n != 0)
return (1); /* success */
rc = 1; /* success */
error:
return (0); /* failure */
if (dynamic_kenv)
uma_zfree(kenv_zone, buf);
return (rc);
}
/*
@ -859,15 +899,17 @@ getenv_ulong(const char *name, unsigned long *data)
int
getenv_quad(const char *name, quad_t *data)
{
char value[KENV_MNAMELEN + 1 + KENV_MVALLEN + 1];
char *vtp;
char *value, *vtp;
quad_t iv;
if (!getenv_string(name, value, sizeof(value)))
value = getenv_string_buffer(name);
if (value == NULL)
return (0);
iv = strtoq(value, &vtp, 0);
if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0'))
if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
freeenv(value);
return (0);
}
switch (vtp[0]) {
case 't': case 'T':
iv *= 1024;
@ -883,8 +925,10 @@ getenv_quad(const char *name, quad_t *data)
case '\0':
break;
default:
freeenv(value);
return (0);
}
freeenv(value);
*data = iv;
return (1);
}