git: a961049fc9b6 - stable/12 - kenv: allow listing of static kernel environments

Kyle Evans kevans at FreeBSD.org
Thu Aug 26 06:44:02 UTC 2021


The branch stable/12 has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=a961049fc9b6e4b14ec071b6c457901b1badbea1

commit a961049fc9b6e4b14ec071b6c457901b1badbea1
Author:     Kyle Evans <kevans at FreeBSD.org>
AuthorDate: 2021-06-20 19:36:10 +0000
Commit:     Kyle Evans <kevans at FreeBSD.org>
CommitDate: 2021-08-26 06:40:45 +0000

    kenv: allow listing of static kernel environments
    
    The early environment is typically cleared, so these new options
    need the PRESERVE_EARLY_KENV kernel config(8) option. These environments
    are reported as missing by kenv(1) if the option is not present in the
    running kernel.
    
    (cherry picked from commit db0f26439357b78863e61985acd1e5acf75ce73d)
---
 bin/kenv/kenv.1             |  18 ++++++
 bin/kenv/kenv.c             |  38 ++++++++++---
 lib/libc/sys/kenv.2         |  24 +++++++-
 sys/kern/kern_environment.c | 131 +++++++++++++++++++++++++++++---------------
 sys/sys/kenv.h              |  10 ++--
 5 files changed, 161 insertions(+), 60 deletions(-)

diff --git a/bin/kenv/kenv.1 b/bin/kenv/kenv.1
index 7a124b7ba756..65e4793617a5 100644
--- a/bin/kenv/kenv.1
+++ b/bin/kenv/kenv.1
@@ -32,6 +32,7 @@
 .Nd list or modify the kernel environment
 .Sh SYNOPSIS
 .Nm
+.Op Fl l | s
 .Op Fl hNq
 .Nm
 .Op Fl qv
@@ -45,6 +46,23 @@ The
 .Nm
 utility will list all variables in the kernel environment if
 invoked without arguments.
+.Pp
+If the
+.Fl l
+option is specified, then the static environment provided by
+.Xr loader 8
+will be listed instead.
+Similarly, the
+.Fl s
+option will list the static environment defined by the kernel config.
+Both of the
+.Fl l
+and
+.Fl s
+options are dependent on the kernel being configured to preserve early kernel
+environments.
+The default kernel configuration does not preserve these environments.
+.Pp
 If the
 .Fl h
 option is specified, it will limit the report to kernel probe hints.
diff --git a/bin/kenv/kenv.c b/bin/kenv/kenv.c
index 77caeaf5bca2..ecf30ee6b617 100644
--- a/bin/kenv/kenv.c
+++ b/bin/kenv/kenv.c
@@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include <err.h>
+#include <errno.h>
 #include <kenv.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -36,14 +37,16 @@ __FBSDID("$FreeBSD$");
 #include <unistd.h>
 
 static void	usage(void);
-static int	kdumpenv(void);
+static int	kdumpenv(int dump_type);
 static int	kgetenv(const char *);
 static int	ksetenv(const char *, char *);
 static int	kunsetenv(const char *);
 
 static int hflag = 0;
+static int lflag = 0;
 static int Nflag = 0;
 static int qflag = 0;
+static int sflag = 0;
 static int uflag = 0;
 static int vflag = 0;
 
@@ -51,7 +54,7 @@ static void
 usage(void)
 {
 	(void)fprintf(stderr, "%s\n%s\n%s\n",
-	    "usage: kenv [-hNq]",
+	    "usage: kenv [-l|-s] [-hNq]",
 	    "       kenv [-qv] variable[=value]",
 	    "       kenv [-q] -u variable");
 	exit(1);
@@ -65,17 +68,23 @@ main(int argc, char **argv)
 
 	val = NULL;
 	env = NULL;
-	while ((ch = getopt(argc, argv, "hNquv")) != -1) {
+	while ((ch = getopt(argc, argv, "hlNqsuv")) != -1) {
 		switch (ch) {
 		case 'h':
 			hflag++;
 			break;
+		case 'l':
+			lflag++;
+			break;
 		case 'N':
 			Nflag++;
 			break;
 		case 'q':
 			qflag++;
 			break;
+		case 's':
+			sflag++;
+			break;
 		case 'u':
 			uflag++;
 			break;
@@ -100,12 +109,23 @@ main(int argc, char **argv)
 	}
 	if ((hflag || Nflag) && env != NULL)
 		usage();
+	if (lflag && sflag)
+		usage();
 	if (argc > 0 || ((uflag || vflag) && env == NULL))
 		usage();
 	if (env == NULL) {
-		error = kdumpenv();
-		if (error && !qflag)
-			warn("kdumpenv");
+		if (lflag)
+			error = kdumpenv(KENV_DUMP_LOADER);
+		else if (sflag)
+			error = kdumpenv(KENV_DUMP_STATIC);
+		else
+			error = kdumpenv(KENV_DUMP);
+		if (error && !qflag) {
+			if (errno == ENOENT)
+				warnx("requested environment is unavailable");
+			else
+				warn("kdumpenv");
+		}
 	} else if (val == NULL) {
 		if (uflag) {
 			error = kunsetenv(env);
@@ -125,12 +145,12 @@ main(int argc, char **argv)
 }
 
 static int
-kdumpenv(void)
+kdumpenv(int dump_type)
 {
 	char *buf, *bp, *cp;
 	int buflen, envlen;
 
-	envlen = kenv(KENV_DUMP, NULL, NULL, 0);
+	envlen = kenv(dump_type, NULL, NULL, 0);
 	if (envlen < 0)
 		return (-1);
 	for (;;) {
@@ -138,7 +158,7 @@ kdumpenv(void)
 		buf = calloc(1, buflen + 1);
 		if (buf == NULL)
 			return (-1);
-		envlen = kenv(KENV_DUMP, NULL, buf, buflen);
+		envlen = kenv(dump_type, NULL, buf, buflen);
 		if (envlen < 0) {
 			free(buf);
 			return (-1);
diff --git a/lib/libc/sys/kenv.2 b/lib/libc/sys/kenv.2
index 06f708170a2f..a1f994569111 100644
--- a/lib/libc/sys/kenv.2
+++ b/lib/libc/sys/kenv.2
@@ -26,7 +26,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 20, 2017
+.Dd June 20, 2021
 .Dt KENV 2
 .Os
 .Sh NAME
@@ -49,7 +49,7 @@ the kernel environment.
 The
 .Fa action
 argument can be one of the following:
-.Bl -tag -width ".Dv KENV_UNSET"
+.Bl -tag -width ".Dv KENV_DUMP_LOADER"
 .It Dv KENV_GET
 Get the
 .Fa value
@@ -90,7 +90,7 @@ and
 arguments are ignored.
 This option is only available to the superuser.
 .It Dv KENV_DUMP
-Dump as much of the kernel environment as will fit in
+Dump as much of the dynamic kernel environment as will fit in
 .Fa value ,
 whose size is given in
 .Fa len .
@@ -103,6 +103,18 @@ will return the number of bytes required to copy out the entire environment.
 The
 .Fa name
 is ignored.
+.It Dv KENV_DUMP_LOADER
+Dump the static environment provided by
+.Xr loader 8 ,
+with semantics identical to
+.Dv KENV_DUMP .
+Duplicate and malformed variables originally present in this environment are
+discarded by the kernel and will not appear in the output.
+.It Dv KENV_DUMP_STATIC
+Dump the static environment defined by the kernel
+.Xr config 5 .
+The semantics are identical to
+.Dv KENV_DUMP_LOADER .
 .El
 .Sh RETURN VALUES
 The
@@ -142,6 +154,12 @@ for a
 .Dv KENV_GET
 or
 .Dv KENV_UNSET .
+.It Bq Er ENOENT
+The requested environment is not available for a
+.Dv KENV_DUMP_LOADER
+or
+.Dv KENV_DUMP_STATIC .
+The kernel is configured to destroy these environments by default.
 .It Bq Er EPERM
 A user other than the superuser attempted to set or unset a kernel
 environment variable.
diff --git a/sys/kern/kern_environment.c b/sys/kern/kern_environment.c
index f37fce450cd9..127435406713 100644
--- a/sys/kern/kern_environment.c
+++ b/sys/kern/kern_environment.c
@@ -91,60 +91,103 @@ bool	dynamic_kenv;
 #define KENV_CHECK	if (!dynamic_kenv) \
 			    panic("%s: called before SI_SUB_KMEM", __func__)
 
+static int
+kenv_dump(struct thread *td, char **envp, int what, char *value, int len)
+{
+	char *buffer, *senv;
+	size_t done, needed, buflen;
+	int error;
+
+	error = 0;
+	buffer = NULL;
+	done = needed = 0;
+
+	MPASS(what == KENV_DUMP || what == KENV_DUMP_LOADER ||
+	    what == KENV_DUMP_STATIC);
+
+	/*
+	 * For non-dynamic kernel environment, we pass in either md_envp or
+	 * kern_envp and we must traverse with kernenv_next().  This shuffling
+	 * of pointers simplifies the below loop by only differing in how envp
+	 * is modified.
+	 */
+	if (what != KENV_DUMP) {
+		senv = (char *)envp;
+		envp = &senv;
+	}
+
+	buflen = len;
+	if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2))
+		buflen = KENV_SIZE * (KENV_MNAMELEN +
+		    kenv_mvallen + 2);
+	if (len > 0 && value != NULL)
+		buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
+
+	/* Only take the lock for the dynamic kenv. */
+	if (what == KENV_DUMP)
+		mtx_lock(&kenv_lock);
+	while (*envp != NULL) {
+		len = strlen(*envp) + 1;
+		needed += len;
+		len = min(len, buflen - done);
+		/*
+		 * If called with a NULL or insufficiently large
+		 * buffer, just keep computing the required size.
+		 */
+		if (value != NULL && buffer != NULL && len > 0) {
+			bcopy(*envp, buffer + done, len);
+			done += len;
+		}
+
+		/* Advance the pointer depending on the kenv format. */
+		if (what == KENV_DUMP)
+			envp++;
+		else
+			senv = kernenv_next(senv);
+	}
+	if (what == KENV_DUMP)
+		mtx_unlock(&kenv_lock);
+	if (buffer != NULL) {
+		error = copyout(buffer, value, done);
+		free(buffer, M_TEMP);
+	}
+	td->td_retval[0] = ((done == needed) ? 0 : needed);
+	return (error);
+}
+
 int
-sys_kenv(td, uap)
-	struct thread *td;
-	struct kenv_args /* {
-		int what;
-		const char *name;
-		char *value;
-		int len;
-	} */ *uap;
-{
-	char *name, *value, *buffer = NULL;
-	size_t len, done, needed, buflen;
-	int error, i;
+sys_kenv(struct thread *td, struct kenv_args *uap)
+{
+	char *name, *value;
+	size_t len;
+	int error;
 
 	KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false"));
 
 	error = 0;
-	if (uap->what == KENV_DUMP) {
+
+	switch (uap->what) {
+	case KENV_DUMP:
 #ifdef MAC
 		error = mac_kenv_check_dump(td->td_ucred);
 		if (error)
 			return (error);
 #endif
-		done = needed = 0;
-		buflen = uap->len;
-		if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2))
-			buflen = KENV_SIZE * (KENV_MNAMELEN +
-			    kenv_mvallen + 2);
-		if (uap->len > 0 && uap->value != NULL)
-			buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
-		mtx_lock(&kenv_lock);
-		for (i = 0; kenvp[i] != NULL; i++) {
-			len = strlen(kenvp[i]) + 1;
-			needed += len;
-			len = min(len, buflen - done);
-			/*
-			 * If called with a NULL or insufficiently large
-			 * buffer, just keep computing the required size.
-			 */
-			if (uap->value != NULL && buffer != NULL && len > 0) {
-				bcopy(kenvp[i], buffer + done, len);
-				done += len;
-			}
-		}
-		mtx_unlock(&kenv_lock);
-		if (buffer != NULL) {
-			error = copyout(buffer, uap->value, done);
-			free(buffer, M_TEMP);
-		}
-		td->td_retval[0] = ((done == needed) ? 0 : needed);
-		return (error);
-	}
-
-	switch (uap->what) {
+		return (kenv_dump(td, kenvp, uap->what, uap->value, uap->len));
+	case KENV_DUMP_LOADER:
+	case KENV_DUMP_STATIC:
+#ifdef MAC
+		error = mac_kenv_check_dump(td->td_ucred);
+		if (error)
+			return (error);
+#endif
+#ifdef PRESERVE_EARLY_KENV
+		return (kenv_dump(td,
+		    uap->what == KENV_DUMP_LOADER ? (char **)md_envp :
+		    (char **)kern_envp, uap->what, uap->value, uap->len));
+#else
+		return (ENOENT);
+#endif
 	case KENV_SET:
 		error = priv_check(td, PRIV_KENV_SET);
 		if (error)
diff --git a/sys/sys/kenv.h b/sys/sys/kenv.h
index fd1ae31f6a95..3920d80c21c7 100644
--- a/sys/sys/kenv.h
+++ b/sys/sys/kenv.h
@@ -34,10 +34,12 @@
 /*
  * Constants for the kenv(2) syscall
  */
-#define KENV_GET	0
-#define KENV_SET	1
-#define KENV_UNSET	2
-#define KENV_DUMP	3
+#define KENV_GET		0
+#define KENV_SET		1
+#define KENV_UNSET		2
+#define KENV_DUMP		3
+#define KENV_DUMP_LOADER	4
+#define KENV_DUMP_STATIC	5
 
 #define KENV_MNAMELEN	128	/* Maximum name length (for the syscall) */
 #define KENV_MVALLEN	128	/* Maximum value length (for the syscall) */


More information about the dev-commits-src-all mailing list