Messages in this thread Patch in this message | | | Date | Thu, 28 May 1998 07:32:29 -0400 | From | Bill Hawes <> | Subject | minor patch for 2.1.103 drivers/char/selection.c |
| |
The attached patch fixes a couple of race conditions in selection.c. The buffer for the selection was being freed first, then kmalloc'ed, leaving a stale reference to th old buffer if the kmalloc blocked. This is fixed by allocating the new buffer first.
In the paste code the buffer reference and length were being carried across a schedule() call if the tty was throttled. I've fixed this by refreshing the buffer and length reference for eash iteration.
Probably the only bad thing that could happen with these problems would be a few garbage characters on the screen, but it seems like we should fix it anyway.
Regards, Bill--- linux-2.1.103/drivers/char/selection.c.old Sat Feb 21 09:24:53 1998 +++ linux-2.1.103/drivers/char/selection.c Wed May 27 17:59:00 1998 @@ -91,12 +91,11 @@ /* set inwordLut contents. Invoked by ioctl(). */ int sel_loadlut(const unsigned long arg) { - int err; + int err = -EFAULT; - err = copy_from_user(inwordLut, (u32 *)(arg+4), 32); - if (err) - return -EFAULT; - return 0; + if (!copy_from_user(inwordLut, (u32 *)(arg+4), 32)) + err = 0; + return err; } /* does screen address p correspond to character at LH/RH edge of screen? */ @@ -226,7 +225,7 @@ /* select to end of line if on trailing space */ if (new_sel_end > new_sel_start && - !atedge(new_sel_end, size_row) && isspace(sel_pos(new_sel_end))) { + !atedge(new_sel_end, size_row) && isspace(sel_pos(new_sel_end))) { for (pe = new_sel_end + 2; ; pe += 2) if (!isspace(sel_pos(pe)) || atedge(pe, size_row)) break; @@ -259,16 +258,18 @@ sel_start = new_sel_start; sel_end = new_sel_end; - if (sel_buffer) - kfree(sel_buffer); - sel_buffer = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL); - if (!sel_buffer) { - printk("selection: kmalloc() failed\n"); + /* Allocate a new buffer before freeing the old one ... */ + bp = kmalloc((sel_end-sel_start)/2+1, GFP_KERNEL); + if (!bp) { + printk(KERN_WARNING "selection: kmalloc() failed\n"); clear_selection(); return -ENOMEM; } + if (sel_buffer) + kfree(sel_buffer); + sel_buffer = bp; - obp = bp = sel_buffer; + obp = bp; for (i = sel_start; i <= sel_end; i += 2) { *bp = sel_pos(i); if (!isspace(*bp++)) @@ -287,31 +288,29 @@ return 0; } -/* Insert the contents of the selection buffer into the queue of the - tty associated with the current console. Invoked by ioctl(). */ +/* Insert the contents of the selection buffer into the + * queue of the tty associated with the current console. + * Invoked by ioctl(). + */ int paste_selection(struct tty_struct *tty) { - struct wait_queue wait = { current, NULL }; - char *bp = sel_buffer; - int c = sel_buffer_lth; - int l; struct vt_struct *vt = (struct vt_struct *) tty->driver_data; + int pasted = 0, count; + struct wait_queue wait = { current, NULL }; - if (!bp || !c) - return 0; poke_blanked_console(); add_wait_queue(&vt->paste_wait, &wait); - do { + while (sel_buffer && sel_buffer_lth > pasted) { current->state = TASK_INTERRUPTIBLE; if (test_bit(TTY_THROTTLED, &tty->flags)) { schedule(); continue; } - l = MIN(c, tty->ldisc.receive_room(tty)); - tty->ldisc.receive_buf(tty, bp, 0, l); - c -= l; - bp += l; - } while (c); + count = sel_buffer_lth - pasted; + count = MIN(count, tty->ldisc.receive_room(tty)); + tty->ldisc.receive_buf(tty, sel_buffer + pasted, 0, count); + pasted += count; + } remove_wait_queue(&vt->paste_wait, &wait); current->state = TASK_RUNNING; return 0; | |