diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-09-09 19:14:02 -0400 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2012-03-24 19:29:53 -0400 |
commit | 31efcebb7d7196adcee73027f513d7c0bf572b47 (patch) | |
tree | c3064342091568bd66f1f5cd85b3c1972ed697da | |
parent | d8c215adbf3901aa7d00a0f17f08d77be689f838 (diff) |
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: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Richard Weinberger <richard@nod.at>
-rw-r--r-- | arch/um/drivers/chan_kern.c | 3 | ||||
-rw-r--r-- | arch/um/drivers/line.c | 54 |
2 files changed, 29 insertions, 28 deletions
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index 420e2c800799..3a95498155a1 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c | |||
@@ -551,6 +551,9 @@ int parse_chan_pair(char *str, struct line *line, int device, | |||
551 | INIT_LIST_HEAD(chans); | 551 | INIT_LIST_HEAD(chans); |
552 | } | 552 | } |
553 | 553 | ||
554 | if (!str) | ||
555 | return 0; | ||
556 | |||
554 | out = strchr(str, ','); | 557 | out = strchr(str, ','); |
555 | if (out != NULL) { | 558 | if (out != NULL) { |
556 | in = str; | 559 | in = str; |
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index dc7e216df6a5..1a8d6591c204 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c | |||
@@ -482,7 +482,7 @@ void close_lines(struct line *lines, int nlines) | |||
482 | } | 482 | } |
483 | 483 | ||
484 | static int setup_one_line(struct line *lines, int n, char *init, | 484 | static int setup_one_line(struct line *lines, int n, char *init, |
485 | char **error_out) | 485 | const struct chan_opts *opts, char **error_out) |
486 | { | 486 | { |
487 | struct line *line = &lines[n]; | 487 | struct line *line = &lines[n]; |
488 | int err = -EINVAL; | 488 | int err = -EINVAL; |
@@ -494,13 +494,28 @@ static int setup_one_line(struct line *lines, int n, char *init, | |||
494 | goto out; | 494 | goto out; |
495 | } | 495 | } |
496 | 496 | ||
497 | if (!strcmp(init, "none")) | 497 | if (!strcmp(init, "none")) { |
498 | line->valid = 0; | 498 | if (line->valid) { |
499 | else { | 499 | line->valid = 0; |
500 | line->init_str = init; | 500 | kfree(line->init_str); |
501 | parse_chan_pair(NULL, line, n, opts, error_out); | ||
502 | err = 0; | ||
503 | } | ||
504 | } else { | ||
505 | char *new = kstrdup(init, GFP_KERNEL); | ||
506 | if (!new) { | ||
507 | *error_out = "Failed to allocate memory"; | ||
508 | return -ENOMEM; | ||
509 | } | ||
510 | line->init_str = new; | ||
501 | line->valid = 1; | 511 | line->valid = 1; |
512 | err = parse_chan_pair(new, line, n, opts, error_out); | ||
513 | if (err) { | ||
514 | line->init_str = NULL; | ||
515 | line->valid = 0; | ||
516 | kfree(new); | ||
517 | } | ||
502 | } | 518 | } |
503 | err = 0; | ||
504 | out: | 519 | out: |
505 | mutex_unlock(&line->count_lock); | 520 | mutex_unlock(&line->count_lock); |
506 | return err; | 521 | return err; |
@@ -549,10 +564,8 @@ out: | |||
549 | int line_config(struct line *lines, unsigned int num, char *str, | 564 | int line_config(struct line *lines, unsigned int num, char *str, |
550 | const struct chan_opts *opts, char **error_out) | 565 | const struct chan_opts *opts, char **error_out) |
551 | { | 566 | { |
552 | struct line *line; | ||
553 | char *new; | ||
554 | char *end; | 567 | char *end; |
555 | int n, err; | 568 | int n; |
556 | 569 | ||
557 | if (*str == '=') { | 570 | if (*str == '=') { |
558 | *error_out = "Can't configure all devices from mconsole"; | 571 | *error_out = "Can't configure all devices from mconsole"; |
@@ -569,16 +582,7 @@ int line_config(struct line *lines, unsigned int num, char *str, | |||
569 | return -EINVAL; | 582 | return -EINVAL; |
570 | } | 583 | } |
571 | 584 | ||
572 | new = kstrdup(end, GFP_KERNEL); | 585 | return setup_one_line(lines, n, end, opts, error_out); |
573 | if (new == NULL) { | ||
574 | *error_out = "Failed to allocate memory"; | ||
575 | return -ENOMEM; | ||
576 | } | ||
577 | err = setup_one_line(lines, n, new, error_out); | ||
578 | if (err) | ||
579 | return err; | ||
580 | line = &lines[n]; | ||
581 | return parse_chan_pair(line->init_str, line, n, opts, error_out); | ||
582 | } | 586 | } |
583 | 587 | ||
584 | int line_get_config(char *name, struct line *lines, unsigned int num, char *str, | 588 | 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) | |||
633 | *error_out = "Device number out of range"; | 637 | *error_out = "Device number out of range"; |
634 | return -EINVAL; | 638 | return -EINVAL; |
635 | } | 639 | } |
636 | return setup_one_line(lines, n, "none", error_out); | 640 | return setup_one_line(lines, n, "none", NULL, error_out); |
637 | } | 641 | } |
638 | 642 | ||
639 | struct tty_driver *register_lines(struct line_driver *line_driver, | 643 | 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) | |||
688 | if (line->init_str == NULL) | 692 | if (line->init_str == NULL) |
689 | continue; | 693 | continue; |
690 | 694 | ||
691 | line->init_str = kstrdup(line->init_str, GFP_KERNEL); | 695 | if (setup_one_line(lines, i, line->init_str, opts, &error)) |
692 | if (line->init_str == NULL) | 696 | printk(KERN_ERR "setup_one_line failed for " |
693 | printk(KERN_ERR "lines_init - kstrdup returned NULL\n"); | ||
694 | |||
695 | if (parse_chan_pair(line->init_str, line, i, opts, &error)) { | ||
696 | printk(KERN_ERR "parse_chan_pair failed for " | ||
697 | "device %d : %s\n", i, error); | 697 | "device %d : %s\n", i, error); |
698 | line->valid = 0; | ||
699 | } | ||
700 | } | 698 | } |
701 | } | 699 | } |
702 | 700 | ||