Commit ca9bda00 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] tty locking on resize

The current kernel serializes console resizes but does not serialize the
resize against the tty structure updates.  This means that while two
parallel resizes cannot mess up the console you can get incorrect results
reported.

Secondly while doing this I added vc_lock_resize() to lock and resize the
console.  This leaves all knowledge of the console_sem in the vt/console
driver and kicks it out of the tty layer, which is good

Thirdly while doing this I decided I couldn't stand "disallocate" any
longer so I switched it to "deallocate".
Signed-off-by: default avatarAlan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent ae78bf9c
...@@ -33,7 +33,7 @@ extern void poke_blanked_console(void); ...@@ -33,7 +33,7 @@ extern void poke_blanked_console(void);
/* Variables for selection control. */ /* Variables for selection control. */
/* Use a dynamic buffer, instead of static (Dec 1994) */ /* Use a dynamic buffer, instead of static (Dec 1994) */
struct vc_data *sel_cons; /* must not be disallocated */ struct vc_data *sel_cons; /* must not be deallocated */
static volatile int sel_start = -1; /* cleared by clear_selection */ static volatile int sel_start = -1; /* cleared by clear_selection */
static int sel_end; static int sel_end;
static int sel_buffer_lth; static int sel_buffer_lth;
......
...@@ -2770,12 +2770,11 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg) ...@@ -2770,12 +2770,11 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
* actually has driver level meaning and triggers a VC resize. * actually has driver level meaning and triggers a VC resize.
* *
* Locking: * Locking:
* The console_sem is used to ensure we do not try and resize * Called function use the console_sem is used to ensure we do
* the console twice at once. * not try and resize the console twice at once.
* FIXME: Two racing size sets may leave the console and kernel * The tty->termios_sem is used to ensure we don't double
* parameters disagreeing. Is this exploitable ? * resize and get confused. Lock order - tty->termios.sem before
* FIXME: Random values racing a window size get is wrong * console sem
* should lock here against that
*/ */
static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
...@@ -2785,18 +2784,18 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, ...@@ -2785,18 +2784,18 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
return -EFAULT; return -EFAULT;
down(&tty->termios_sem);
if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg))) if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
return 0; goto done;
#ifdef CONFIG_VT #ifdef CONFIG_VT
if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) { if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
int rc; if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row)) {
up(&tty->termios_sem);
acquire_console_sem();
rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row);
release_console_sem();
if (rc)
return -ENXIO; return -ENXIO;
} }
}
#endif #endif
if (tty->pgrp > 0) if (tty->pgrp > 0)
kill_pg(tty->pgrp, SIGWINCH, 1); kill_pg(tty->pgrp, SIGWINCH, 1);
...@@ -2804,6 +2803,8 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, ...@@ -2804,6 +2803,8 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
kill_pg(real_tty->pgrp, SIGWINCH, 1); kill_pg(real_tty->pgrp, SIGWINCH, 1);
tty->winsize = tmp_ws; tty->winsize = tmp_ws;
real_tty->winsize = tmp_ws; real_tty->winsize = tmp_ws;
done:
up(&tty->termios_sem);
return 0; return 0;
} }
......
...@@ -885,8 +885,17 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) ...@@ -885,8 +885,17 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
return err; return err;
} }
int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
{
int rc;
acquire_console_sem();
rc = vc_resize(vc, cols, lines);
release_console_sem();
return rc;
}
void vc_disallocate(unsigned int currcons) void vc_deallocate(unsigned int currcons)
{ {
WARN_CONSOLE_UNLOCKED(); WARN_CONSOLE_UNLOCKED();
...@@ -3790,6 +3799,7 @@ EXPORT_SYMBOL(default_blu); ...@@ -3790,6 +3799,7 @@ EXPORT_SYMBOL(default_blu);
EXPORT_SYMBOL(update_region); EXPORT_SYMBOL(update_region);
EXPORT_SYMBOL(redraw_screen); EXPORT_SYMBOL(redraw_screen);
EXPORT_SYMBOL(vc_resize); EXPORT_SYMBOL(vc_resize);
EXPORT_SYMBOL(vc_lock_resize);
EXPORT_SYMBOL(fg_console); EXPORT_SYMBOL(fg_console);
EXPORT_SYMBOL(console_blank_hook); EXPORT_SYMBOL(console_blank_hook);
EXPORT_SYMBOL(console_blanked); EXPORT_SYMBOL(console_blanked);
......
...@@ -96,7 +96,7 @@ do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_str ...@@ -96,7 +96,7 @@ do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_str
if (!perm) if (!perm)
return -EPERM; return -EPERM;
if (!i && v == K_NOSUCHMAP) { if (!i && v == K_NOSUCHMAP) {
/* disallocate map */ /* deallocate map */
key_map = key_maps[s]; key_map = key_maps[s];
if (s && key_map) { if (s && key_map) {
key_maps[s] = NULL; key_maps[s] = NULL;
...@@ -819,20 +819,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, ...@@ -819,20 +819,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (arg > MAX_NR_CONSOLES) if (arg > MAX_NR_CONSOLES)
return -ENXIO; return -ENXIO;
if (arg == 0) { if (arg == 0) {
/* disallocate all unused consoles, but leave 0 */ /* deallocate all unused consoles, but leave 0 */
acquire_console_sem(); acquire_console_sem();
for (i=1; i<MAX_NR_CONSOLES; i++) for (i=1; i<MAX_NR_CONSOLES; i++)
if (! VT_BUSY(i)) if (! VT_BUSY(i))
vc_disallocate(i); vc_deallocate(i);
release_console_sem(); release_console_sem();
} else { } else {
/* disallocate a single console, if possible */ /* deallocate a single console, if possible */
arg--; arg--;
if (VT_BUSY(arg)) if (VT_BUSY(arg))
return -EBUSY; return -EBUSY;
if (arg) { /* leave 0 */ if (arg) { /* leave 0 */
acquire_console_sem(); acquire_console_sem();
vc_disallocate(arg); vc_deallocate(arg);
release_console_sem(); release_console_sem();
} }
} }
...@@ -847,11 +847,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, ...@@ -847,11 +847,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (get_user(ll, &vtsizes->v_rows) || if (get_user(ll, &vtsizes->v_rows) ||
get_user(cc, &vtsizes->v_cols)) get_user(cc, &vtsizes->v_cols))
return -EFAULT; return -EFAULT;
for (i = 0; i < MAX_NR_CONSOLES; i++) { for (i = 0; i < MAX_NR_CONSOLES; i++)
acquire_console_sem(); vc_lock_resize(vc_cons[i].d, cc, ll);
vc_resize(vc_cons[i].d, cc, ll);
release_console_sem();
}
return 0; return 0;
} }
......
...@@ -33,7 +33,8 @@ extern int fg_console, last_console, want_console; ...@@ -33,7 +33,8 @@ extern int fg_console, last_console, want_console;
int vc_allocate(unsigned int console); int vc_allocate(unsigned int console);
int vc_cons_allocated(unsigned int console); int vc_cons_allocated(unsigned int console);
int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines); int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines);
void vc_disallocate(unsigned int console); int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines);
void vc_deallocate(unsigned int console);
void reset_palette(struct vc_data *vc); void reset_palette(struct vc_data *vc);
void do_blank_screen(int entering_gfx); void do_blank_screen(int entering_gfx);
void do_unblank_screen(int leaving_gfx); void do_unblank_screen(int leaving_gfx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment