Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177461015
D61163.1794469230.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
14 KB
Referenced Files
None
Subscribers
None
D61163.1794469230.diff
View Options
diff --git a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
--- a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
+++ b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
@@ -57,6 +57,8 @@
#include "uart_emul.h"
static const struct config_option config_options_machdep[] = {
+ { "acpi_tables", CONFIG_OPTION_BOOL },
+ { "acpi_tables_in_memory", CONFIG_OPTION_BOOL },
{ "gdb.address", CONFIG_OPTION_STRING },
{ "gdb.port", CONFIG_OPTION_INTEGER },
{ "gdb.wait", CONFIG_OPTION_BOOL },
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -78,6 +78,7 @@
#include "bhyverun.h"
#include "bootrom.h"
#include "config.h"
+#include "config_schema.h"
#include "debug.h"
#ifdef BHYVE_GDB
#include "gdb.h"
@@ -869,6 +870,7 @@
#endif
bhyve_init_config();
+ set_config_path_validator(config_schema_path_valid);
bhyve_optparse(argc, argv);
argc -= optind;
argv += optind;
diff --git a/usr.sbin/bhyve/config.h b/usr.sbin/bhyve/config.h
--- a/usr.sbin/bhyve/config.h
+++ b/usr.sbin/bhyve/config.h
@@ -54,6 +54,12 @@
int walk_config_nodes(const char *, const nvlist_t *, void *,
int (*cb)(const char *, const nvlist_t *, const char *, int, void *));
+/*
+ * Installs a callback used to validate configuration variables fetched by
+ * bhyve. Raw lookups used to expand variable references are not validated.
+ */
+void set_config_path_validator(bool (*)(const char *));
+
/*
* Fetches the value of a configuration variable. If the "raw" value
* contains references to other configuration variables, this function
@@ -64,6 +70,8 @@
* If no node is found, returns NULL.
*
* If 'parent' is NULL, 'name' is assumed to be a top-level variable.
+ * Configuration consumers must use these accessors rather than reading
+ * string values directly from the backing nvlist so that reads are validated.
*/
const char *get_config_value_node(const nvlist_t *parent, const char *name);
diff --git a/usr.sbin/bhyve/config.c b/usr.sbin/bhyve/config.c
--- a/usr.sbin/bhyve/config.c
+++ b/usr.sbin/bhyve/config.c
@@ -27,6 +27,7 @@
*/
#include <sys/cdefs.h>
+#include <sys/queue.h>
#include <assert.h>
#include <err.h>
#include <stdio.h>
@@ -36,14 +37,90 @@
#include "config.h"
static nvlist_t *config_root;
+static bool (*config_path_validator)(const char *);
+
+struct config_node_path {
+ SLIST_ENTRY(config_node_path) link;
+ const nvlist_t *node;
+ char *path;
+};
+static SLIST_HEAD(, config_node_path) config_node_paths;
+
+static const char *
+config_node_path(const nvlist_t *node)
+{
+ struct config_node_path *entry;
+
+ SLIST_FOREACH(entry, &config_node_paths, link) {
+ if (entry->node == node)
+ return (entry->path);
+ }
+ return (NULL);
+}
+
+static void
+register_config_node(const nvlist_t *node, const char *path)
+{
+ struct config_node_path *entry;
+
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL)
+ err(4, "Failed to allocate memory");
+ entry->path = strdup(path);
+ if (entry->path == NULL)
+ err(4, "Failed to allocate memory");
+ entry->node = node;
+ SLIST_INSERT_HEAD(&config_node_paths, entry, link);
+}
+
+static void
+register_config_child(const nvlist_t *parent, const nvlist_t *child,
+ const char *name)
+{
+ const char *parent_path;
+ char *path;
+
+ parent_path = config_node_path(parent);
+ if (parent_path == NULL) {
+ if (config_path_validator != NULL)
+ errx(4,
+ "cannot register child of unregistered configuration node");
+ return;
+ }
+ if (*parent_path == '\0') {
+ if (asprintf(&path, "%s", name) == -1)
+ err(4, "Failed to allocate memory");
+ } else if (asprintf(&path, "%s.%s", parent_path, name) == -1)
+ err(4, "Failed to allocate memory");
+ register_config_node(child, path);
+ free(path);
+}
+
+void
+set_config_path_validator(bool (*validator)(const char *))
+{
+
+ config_path_validator = validator;
+}
+
+static void
+validate_config_path(const char *path)
+{
+
+ if (config_path_validator != NULL && !config_path_validator(path))
+ errx(4, "unsupported configuration variable '%s'", path);
+}
void
init_config(void)
{
+ SLIST_INIT(&config_node_paths);
+ config_path_validator = NULL;
config_root = nvlist_create(0);
if (config_root == NULL)
err(4, "Failed to create configuration root nvlist");
+ register_config_node(config_root, "");
}
static nvlist_t *
@@ -93,6 +170,7 @@
if (new_nvl == NULL)
errx(4, "Failed to allocate memory");
nvlist_move_nvlist(nvl, name, new_nvl);
+ register_config_child(nvl, new_nvl, name);
nvl = new_nvl;
} else {
nvl = NULL;
@@ -336,6 +414,7 @@
{
const char *value;
+ validate_config_path(path);
value = get_raw_config_value(path);
if (value == NULL)
return (NULL);
@@ -345,11 +424,26 @@
const char *
get_config_value_node(const nvlist_t *parent, const char *name)
{
+ const char *parent_path;
+ char *path;
if (strchr(name, '.') != NULL)
errx(4, "Invalid config node name %s", name);
if (parent == NULL)
parent = config_root;
+ if (config_path_validator != NULL) {
+ parent_path = config_node_path(parent);
+ if (parent_path == NULL)
+ errx(4,
+ "cannot validate unregistered configuration node");
+ if (*parent_path == '\0') {
+ if (asprintf(&path, "%s", name) == -1)
+ err(4, "Failed to allocate memory");
+ } else if (asprintf(&path, "%s.%s", parent_path, name) == -1)
+ err(4, "Failed to allocate memory");
+ validate_config_path(path);
+ free(path);
+ }
if (nvlist_exists_nvlist(parent, name))
warnx("Attempt to fetch value of node %s of list %p", name,
parent);
diff --git a/usr.sbin/bhyve/config_schema.h b/usr.sbin/bhyve/config_schema.h
--- a/usr.sbin/bhyve/config_schema.h
+++ b/usr.sbin/bhyve/config_schema.h
@@ -10,6 +10,8 @@
#include <sys/linker_set.h>
#include <sys/types.h>
+#include <stdbool.h>
+
enum config_option_type {
CONFIG_OPTION_BOOL,
CONFIG_OPTION_INTEGER,
@@ -35,6 +37,9 @@
void config_schema_merge_options(struct config_schema *, const char *,
const struct config_option *, size_t);
void config_schema_merge_options_machdep(struct config_schema *);
+/* Look up an exact option name or a concrete instance of a schema pattern. */
+bool config_schema_lookup(const char *, enum config_option_type *);
+bool config_schema_path_valid(const char *);
void print_config_options(void);
#endif /* !__CONFIG_SCHEMA_H__ */
diff --git a/usr.sbin/bhyve/config_schema.c b/usr.sbin/bhyve/config_schema.c
--- a/usr.sbin/bhyve/config_schema.c
+++ b/usr.sbin/bhyve/config_schema.c
@@ -8,6 +8,7 @@
#include <sys/param.h>
#include <sys/tree.h>
+#include <ctype.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
@@ -18,10 +19,13 @@
SET_DECLARE(config_option_set, struct config_option_set);
+#define CONFIG_OPTION_HIDDEN (1 << 0)
+
struct config_schema_option {
RB_ENTRY(config_schema_option) entry;
char *name;
enum config_option_type type;
+ unsigned int flags;
};
RB_HEAD(config_schema, config_schema_option);
@@ -107,6 +111,15 @@
{ "pci.B.S.F.slot.N.device", CONFIG_OPTION_STRING },
};
+static const struct config_option config_compat_options[] = {
+#ifdef __amd64__
+ { "lpc.bootrom", CONFIG_OPTION_STRING },
+ { "lpc.bootvars", CONFIG_OPTION_STRING },
+#endif
+ { "virtio_msix", CONFIG_OPTION_BOOL },
+ { "monitor", CONFIG_OPTION_BOOL },
+};
+
static const char *
config_option_type_name(enum config_option_type type)
{
@@ -122,9 +135,10 @@
errx(EX_SOFTWARE, "invalid configuration option type");
}
-void
-config_schema_merge_options(struct config_schema *schema, const char *prefix,
- const struct config_option *options, size_t count)
+static void
+config_schema_merge_options_flags(struct config_schema *schema,
+ const char *prefix, const struct config_option *options, size_t count,
+ unsigned int flags)
{
struct config_schema_option *existing, *option;
char *name;
@@ -140,6 +154,7 @@
err(EX_OSERR, "malloc");
option->name = name;
option->type = options[i].type;
+ option->flags = flags;
existing = RB_INSERT(config_schema, schema, option);
if (existing == NULL)
@@ -151,11 +166,24 @@
config_option_type_name(existing->type),
config_option_type_name(option->type));
}
+ if (existing->flags != option->flags) {
+ errx(EX_SOFTWARE,
+ "conflicting flags for configuration option '%s'",
+ option->name);
+ }
free(option->name);
free(option);
}
}
+void
+config_schema_merge_options(struct config_schema *schema, const char *prefix,
+ const struct config_option *options, size_t count)
+{
+
+ config_schema_merge_options_flags(schema, prefix, options, count, 0);
+}
+
static void
config_schema_merge_option_set(struct config_schema *schema,
const struct config_option_set *set, const char *inherited_prefix)
@@ -168,35 +196,115 @@
config_schema_merge_options(schema, prefix, set->options, set->count);
}
-static void
-config_schema_destroy(struct config_schema *schema)
+static struct config_schema *
+config_schema_get(void)
{
- struct config_schema_option *option, *tmp;
+ static struct config_schema schema;
+ static bool initialized;
+ struct config_option_set **setp;
- RB_FOREACH_SAFE(option, config_schema, schema, tmp) {
- RB_REMOVE(config_schema, schema, option);
- free(option->name);
- free(option);
+ if (!initialized) {
+ RB_INIT(&schema);
+ config_schema_merge_options(&schema, NULL, config_options,
+ nitems(config_options));
+ config_schema_merge_options_flags(&schema, NULL,
+ config_compat_options, nitems(config_compat_options),
+ CONFIG_OPTION_HIDDEN);
+ SET_FOREACH(setp, config_option_set) {
+ config_schema_merge_option_set(&schema, *setp, NULL);
+ }
+ config_schema_merge_options_machdep(&schema);
+ initialized = true;
+ }
+ return (&schema);
+}
+
+static bool
+config_schema_component_matches(const char *pattern, size_t pattern_len,
+ const char *value, size_t value_len)
+{
+ size_t i;
+
+ /* '*' matches one component; N, B, S, and F match decimal numbers. */
+ if (pattern_len == 1 && pattern[0] == '*')
+ return (value_len != 0);
+ if (pattern_len == 1 && strchr("NBSF", pattern[0]) != NULL) {
+ if (value_len == 0)
+ return (false);
+ for (i = 0; i < value_len; i++) {
+ if (!isdigit((unsigned char)value[i]))
+ return (false);
+ }
+ return (true);
+ }
+ return (pattern_len == value_len &&
+ strncmp(pattern, value, pattern_len) == 0);
+}
+
+static bool
+config_schema_path_matches(const char *pattern, const char *path)
+{
+ const char *pattern_end, *path_end;
+ size_t pattern_len, path_len;
+
+ for (;;) {
+ pattern_end = strchr(pattern, '.');
+ path_end = strchr(path, '.');
+ pattern_len = pattern_end != NULL ?
+ (size_t)(pattern_end - pattern) : strlen(pattern);
+ path_len = path_end != NULL ? (size_t)(path_end - path) :
+ strlen(path);
+ if (!config_schema_component_matches(pattern, pattern_len, path,
+ path_len))
+ return (false);
+ if (pattern_end == NULL || path_end == NULL)
+ return (pattern_end == NULL && path_end == NULL);
+ pattern = pattern_end + 1;
+ path = path_end + 1;
+ }
+}
+
+bool
+config_schema_lookup(const char *path, enum config_option_type *type)
+{
+ struct config_schema_option key, *option;
+ struct config_schema *schema;
+
+ schema = config_schema_get();
+ key.name = __DECONST(char *, path);
+ option = RB_FIND(config_schema, schema, &key);
+ if (option != NULL) {
+ if (type != NULL)
+ *type = option->type;
+ return (true);
+ }
+
+ RB_FOREACH(option, config_schema, schema) {
+ if (config_schema_path_matches(option->name, path)) {
+ if (type != NULL)
+ *type = option->type;
+ return (true);
+ }
}
+ return (false);
+}
+
+bool
+config_schema_path_valid(const char *path)
+{
+
+ return (config_schema_lookup(path, NULL));
}
void
print_config_options(void)
{
struct config_schema_option *option;
- struct config_option_set **setp;
- struct config_schema schema;
-
- RB_INIT(&schema);
- config_schema_merge_options(&schema, NULL, config_options,
- nitems(config_options));
- SET_FOREACH(setp, config_option_set) {
- config_schema_merge_option_set(&schema, *setp, NULL);
- }
- config_schema_merge_options_machdep(&schema);
+ struct config_schema *schema;
- RB_FOREACH(option, config_schema, &schema) {
- printf("%s\n", option->name);
+ schema = config_schema_get();
+ RB_FOREACH(option, config_schema, schema) {
+ if ((option->flags & CONFIG_OPTION_HIDDEN) == 0)
+ printf("%s\n", option->name);
}
- config_schema_destroy(&schema);
}
diff --git a/usr.sbin/bhyve/net_backend_slirp.c b/usr.sbin/bhyve/net_backend_slirp.c
--- a/usr.sbin/bhyve/net_backend_slirp.c
+++ b/usr.sbin/bhyve/net_backend_slirp.c
@@ -124,13 +124,7 @@
goto err;
}
- config = nvlist_clone(nvl);
- if (config == NULL) {
- EPRINTLN("nvlist_clone");
- goto err;
- }
-
- mtu_value = get_config_value_node(config, "mtu");
+ mtu_value = get_config_value_node(nvl, "mtu");
if (mtu_value != NULL) {
if (net_parsemtu(mtu_value, &mtu)) {
EPRINTLN("Could not parse MTU");
@@ -139,6 +133,12 @@
} else {
mtu = DEFAULT_MTU;
}
+
+ config = nvlist_clone(nvl);
+ if (config == NULL) {
+ EPRINTLN("nvlist_clone");
+ goto err;
+ }
nvlist_add_number(config, "mtui", mtu);
priv->mtu = mtu;
diff --git a/usr.sbin/bhyve/pci_virtio_scsi.c b/usr.sbin/bhyve/pci_virtio_scsi.c
--- a/usr.sbin/bhyve/pci_virtio_scsi.c
+++ b/usr.sbin/bhyve/pci_virtio_scsi.c
@@ -1230,7 +1230,8 @@
/*
* 'value' contains the backend path. Call the backend to open it.
*/
- value = nvlist_get_string(parent, name);
+ value = get_config_value_node(parent, name);
+ assert(value != NULL);
ret = sc->vss_backend->vsb_open(sc, value, target);
if (ret != 0)
EPRINTLN("cannot open target %lu at %s: %s", target, value,
diff --git a/usr.sbin/bhyve/riscv/bhyverun_machdep.c b/usr.sbin/bhyve/riscv/bhyverun_machdep.c
--- a/usr.sbin/bhyve/riscv/bhyverun_machdep.c
+++ b/usr.sbin/bhyve/riscv/bhyverun_machdep.c
@@ -60,6 +60,8 @@
#include "riscv.h"
static const struct config_option config_options_machdep[] = {
+ { "acpi_tables", CONFIG_OPTION_BOOL },
+ { "acpi_tables_in_memory", CONFIG_OPTION_BOOL },
{ "console", CONFIG_OPTION_STRING },
{ "fdt.bootargs", CONFIG_OPTION_STRING },
};
diff --git a/usr.sbin/bhyve/smbiostbl.c b/usr.sbin/bhyve/smbiostbl.c
--- a/usr.sbin/bhyve/smbiostbl.c
+++ b/usr.sbin/bhyve/smbiostbl.c
@@ -836,7 +836,7 @@
snprintf(keystr, sizeof(keystr), "%d", key);
if (nvlist_exists(node, keystr))
- vp = nvlist_get_string(node, keystr);
+ vp = get_config_value_node(node, keystr);
else
vp = na;
strings[key - 1].value = strdup(vp);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Nov 12, 7:40 AM (16 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41661158
Default Alt Text
D61163.1794469230.diff (14 KB)
Attached To
Mode
D61163: bhyve: validate configuration options
Attached
Detach File
Event Timeline