Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31efcebb authored by Al Viro's avatar Al Viro Committed by Richard Weinberger
Browse files

um: fix races between line_open() and line_config()



Pull parse_chan_pair() call into setup_one_line(), under the mutex.
We really don't want open() to succeed before parse_chan_pair() had
been done (or after it has failed, BTW).  We also want "remove con<n>"
to free irqs, etc., same as "config con<n>=none".

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent d8c215ad
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -551,6 +551,9 @@ int parse_chan_pair(char *str, struct line *line, int device,
		INIT_LIST_HEAD(chans);
		INIT_LIST_HEAD(chans);
	}
	}


	if (!str)
		return 0;

	out = strchr(str, ',');
	out = strchr(str, ',');
	if (out != NULL) {
	if (out != NULL) {
		in = str;
		in = str;
+26 −28
Original line number Original line Diff line number Diff line
@@ -482,7 +482,7 @@ void close_lines(struct line *lines, int nlines)
}
}


static int setup_one_line(struct line *lines, int n, char *init,
static int setup_one_line(struct line *lines, int n, char *init,
			  char **error_out)
			  const struct chan_opts *opts, char **error_out)
{
{
	struct line *line = &lines[n];
	struct line *line = &lines[n];
	int err = -EINVAL;
	int err = -EINVAL;
@@ -494,13 +494,28 @@ static int setup_one_line(struct line *lines, int n, char *init,
		goto out;
		goto out;
	}
	}


	if (!strcmp(init, "none"))
	if (!strcmp(init, "none")) {
		if (line->valid) {
			line->valid = 0;
			line->valid = 0;
	else {
			kfree(line->init_str);
		line->init_str = init;
			parse_chan_pair(NULL, line, n, opts, error_out);
			err = 0;
		}
	} else {
		char *new = kstrdup(init, GFP_KERNEL);
		if (!new) {
			*error_out = "Failed to allocate memory";
			return -ENOMEM;
		}
		line->init_str = new;
		line->valid = 1;
		line->valid = 1;
		err = parse_chan_pair(new, line, n, opts, error_out);
		if (err) {
			line->init_str = NULL;
			line->valid = 0;
			kfree(new);
		}
	}
	}
	err = 0;
out:
out:
	mutex_unlock(&line->count_lock);
	mutex_unlock(&line->count_lock);
	return err;
	return err;
@@ -549,10 +564,8 @@ int line_setup(char **conf, unsigned int num, char **def,
int line_config(struct line *lines, unsigned int num, char *str,
int line_config(struct line *lines, unsigned int num, char *str,
		const struct chan_opts *opts, char **error_out)
		const struct chan_opts *opts, char **error_out)
{
{
	struct line *line;
	char *new;
	char *end;
	char *end;
	int n, err;
	int n;


	if (*str == '=') {
	if (*str == '=') {
		*error_out = "Can't configure all devices from mconsole";
		*error_out = "Can't configure all devices from mconsole";
@@ -569,16 +582,7 @@ int line_config(struct line *lines, unsigned int num, char *str,
		return -EINVAL;
		return -EINVAL;
	}
	}


	new = kstrdup(end, GFP_KERNEL);
	return setup_one_line(lines, n, end, opts, error_out);
	if (new == NULL) {
		*error_out = "Failed to allocate memory";
		return -ENOMEM;
	}
	err = setup_one_line(lines, n, new, error_out);
	if (err)
		return err;
	line = &lines[n];
	return parse_chan_pair(line->init_str, line, n, opts, error_out);
}
}


int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
@@ -633,7 +637,7 @@ int line_remove(struct line *lines, unsigned int num, int n, char **error_out)
		*error_out = "Device number out of range";
		*error_out = "Device number out of range";
		return -EINVAL;
		return -EINVAL;
	}
	}
	return setup_one_line(lines, n, "none", error_out);
	return setup_one_line(lines, n, "none", NULL, error_out);
}
}


struct tty_driver *register_lines(struct line_driver *line_driver,
struct tty_driver *register_lines(struct line_driver *line_driver,
@@ -688,15 +692,9 @@ void lines_init(struct line *lines, int nlines, struct chan_opts *opts)
		if (line->init_str == NULL)
		if (line->init_str == NULL)
			continue;
			continue;


		line->init_str = kstrdup(line->init_str, GFP_KERNEL);
		if (setup_one_line(lines, i, line->init_str, opts, &error))
		if (line->init_str == NULL)
			printk(KERN_ERR "setup_one_line failed for "
			printk(KERN_ERR "lines_init - kstrdup returned NULL\n");

		if (parse_chan_pair(line->init_str, line, i, opts, &error)) {
			printk(KERN_ERR "parse_chan_pair failed for "
			       "device %d : %s\n", i, error);
			       "device %d : %s\n", i, error);
			line->valid = 0;
		}
	}
	}
}
}