aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorKyle Evans <kevans@FreeBSD.org>2021-06-20 19:36:10 +0000
committerKyle Evans <kevans@FreeBSD.org>2021-07-19 04:06:19 +0000
commitdb0f26439357b78863e61985acd1e5acf75ce73d (patch)
tree9a30a6481206e81ec1b726294398fdd41c88896c /sys
parent7a129c973b5ba0fa916dfa658d523bec66dbd02d (diff)
downloadsrc-db0f26439357b78863e61985acd1e5acf75ce73d.tar.gz
src-db0f26439357b78863e61985acd1e5acf75ce73d.zip
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. Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D30835
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/kern_environment.c129
-rw-r--r--sys/sys/kenv.h10
2 files changed, 92 insertions, 47 deletions
diff --git a/sys/kern/kern_environment.c b/sys/kern/kern_environment.c
index 8dc345559e95..2a4c62d64a0f 100644
--- a/sys/kern/kern_environment.c
+++ b/sys/kern/kern_environment.c
@@ -92,60 +92,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;
+sys_kenv(struct thread *td, struct kenv_args *uap)
{
- char *name, *value, *buffer = NULL;
- size_t len, done, needed, buflen;
- int error, i;
+ 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 4c851631f343..eccdc027adcc 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) */