[lxc-devel] [PATCH] lxc-ls: try to protect stack in recursive function

Christian Brauner christian.brauner at mailbox.org
Wed Jan 13 20:23:00 UTC 2016


As ls_get() is non-tail recursive we face the inherent danger of blowing up the
stack at some level of nesting. To have at least some security we define
MAX_NESTLVL to be 5. That should be sufficient for most users. The argument lvl
to ls_get() can be used to keep track of the level of nesting we are at. If lvl
is greater than the allowed default level return (without error) and unwind the
stack.

--nesting gains an optional numeric argument. This allows the user to specify
the maximum level of nesting she/he wants to see. Fair warning: If your nesting
level is really deep and/or you have a lot of containers your might run into
trouble.

Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
 src/lxc/arguments.h |  2 +-
 src/lxc/lxc_ls.c    | 37 ++++++++++++++++++++++++++++++++++---
 2 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h
index 898a9ce..a3d2932 100644
--- a/src/lxc/arguments.h
+++ b/src/lxc/arguments.h
@@ -124,11 +124,11 @@ struct lxc_arguments {
 	char *ls_fancy_format;
 	char *ls_groups;
 	char *ls_regex;
+	unsigned int ls_nesting; /* maximum allowed nesting level */
 	bool ls_active;
 	bool ls_fancy;
 	bool ls_frozen;
 	bool ls_line;
-	bool ls_nesting;
 	bool ls_running;
 	bool ls_stopped;
 
diff --git a/src/lxc/lxc_ls.c b/src/lxc/lxc_ls.c
index 3a7fa15..a4ab9b6 100644
--- a/src/lxc/lxc_ls.c
+++ b/src/lxc/lxc_ls.c
@@ -40,6 +40,10 @@
 lxc_log_define(lxc_ls, lxc);
 
 #define LINELEN 1024
+/* Per default we only allow five levels of recursion to protect the stack at
+ * least a little bit. */
+#define MAX_NESTLVL 5
+
 #define LS_FROZEN 1
 #define LS_STOPPED 2
 #define LS_ACTIVE 3
@@ -154,7 +158,7 @@ static const struct option my_longopts[] = {
 	{"running", no_argument, 0, LS_RUNNING},
 	{"frozen", no_argument, 0, LS_FROZEN},
 	{"stopped", no_argument, 0, LS_STOPPED},
-	{"nesting", no_argument, 0, LS_NESTING},
+	{"nesting", optional_argument, 0, LS_NESTING},
 	{"groups", required_argument, 0, 'g'},
 	{"regex", required_argument, 0, 'r'},
 	LXC_COMMON_OPTIONS
@@ -177,11 +181,12 @@ Options :\n\
   --running          list only running containers\n\
   --frozen           list only frozen containers\n\
   --stopped          list only stopped containers\n\
-  --nesting          list nested containers\n\
+  --nesting=NUM      list nested containers up to NUM (default is 5) levels of nesting\n\
   -r --regex         filter container names by regular expression\n\
   -g --groups        comma separated list of groups a container must have to be displayed\n",
 	.options = my_longopts,
 	.parser = my_parser,
+	.ls_nesting = MAX_NESTLVL,
 };
 
 int main(int argc, char *argv[])
@@ -300,6 +305,16 @@ static int ls_get(struct ls **m, size_t *size, const struct lxc_arguments *args,
 		struct lengths *lht, const char *basepath, const char *parent,
 		unsigned int lvl)
 {
+	/* As ls_get() is non-tail recursive we face the inherent danger of
+	 * blowing up the stack at some level of nesting. To have at least some
+	 * security we define MAX_NESTLVL to be 5. That should be sufficient for
+	 * most users. The argument lvl can be used to keep track of the level
+	 * of nesting we are at. If lvl is greater than the allowed default
+	 * level or the level the user specified on the command line we return
+	 * and unwind the stack. */
+	if (lvl > args->ls_nesting)
+		return 0;
+
 	int num = 0, ret = -1;
 	char **containers = NULL;
 	/* If we, at some level of nesting, encounter a stopped container but
@@ -855,6 +870,8 @@ static void ls_print_table(struct ls *l, struct lengths *lht,
 
 static int my_parser(struct lxc_arguments *args, int c, char *arg)
 {
+	char *invalid;
+	unsigned long int m, n = MAX_NESTLVL;
 	switch (c) {
 	case '1':
 		args->ls_line = true;
@@ -875,7 +892,21 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg)
 		args->ls_stopped = true;
 		break;
 	case LS_NESTING:
-		args->ls_nesting = true;
+		/* In case strtoul() receives a string that represents a
+		 * negative number it will return ULONG_MAX - the number that
+		 * string represents if the number the string represents is <
+		 * ULONG_MAX and ULONG_MAX otherwise. But it will consider this
+		 * valid input and not set errno. So we check manually if the
+		 * first character of num_string == '-'. Otherwise the default
+		 * level remains set. */
+		if (arg && !(*arg == '-')) {
+			errno = 0;
+			m = strtoul(arg, &invalid, 0);
+			/* ls_nesting has type unsigned int. */
+			if (!errno && (*invalid == '\0') && (m <= UINT_MAX))
+				n = m;
+		}
+		args->ls_nesting = n;
 		break;
 	case 'g':
 		args->groups = arg;
-- 
2.7.0



More information about the lxc-devel mailing list