diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 33214d92ca4cc4d255fb09f576019984adca1077..5dfbfa7d7606baf4f3f5ee92bd690e7c1b74c4fd 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2910,6 +2910,9 @@ static const struct tty_operations con_ops = { .flush_chars = con_flush_chars, .chars_in_buffer = con_chars_in_buffer, .ioctl = vt_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vt_compat_ioctl, +#endif .stop = con_stop, .start = con_start, .throttle = con_throttle, diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 0fceb8f4d6f22622b5d8f70db707b99667eece2c..30b5d128037c1a78adf183ae280ebf059ad9c187 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1376,6 +1377,208 @@ void vc_SAK(struct work_struct *work) release_console_sem(); } +#ifdef CONFIG_COMPAT + +struct compat_consolefontdesc { + unsigned short charcount; /* characters in font (256 or 512) */ + unsigned short charheight; /* scan lines per character (1-32) */ + compat_caddr_t chardata; /* font data in expanded form */ +}; + +static inline int +compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd, + int perm, struct console_font_op *op) +{ + struct compat_consolefontdesc cfdarg; + int i; + + if (copy_from_user(&cfdarg, user_cfd, sizeof(struct compat_consolefontdesc))) + return -EFAULT; + + switch (cmd) { + case PIO_FONTX: + if (!perm) + return -EPERM; + op->op = KD_FONT_OP_SET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = compat_ptr(cfdarg.chardata); + return con_font_op(vc_cons[fg_console].d, op); + case GIO_FONTX: + op->op = KD_FONT_OP_GET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = compat_ptr(cfdarg.chardata); + i = con_font_op(vc_cons[fg_console].d, op); + if (i) + return i; + cfdarg.charheight = op->height; + cfdarg.charcount = op->charcount; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct compat_consolefontdesc))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +struct compat_console_font_op { + compat_uint_t op; /* operation code KD_FONT_OP_* */ + compat_uint_t flags; /* KD_FONT_FLAG_* */ + compat_uint_t width, height; /* font size */ + compat_uint_t charcount; + compat_caddr_t data; /* font data with height fixed to 32 */ +}; + +static inline int +compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, + int perm, struct console_font_op *op, struct vc_data *vc) +{ + int i; + + if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op))) + return -EFAULT; + if (!perm && op->op != KD_FONT_OP_GET) + return -EPERM; + op->data = compat_ptr(((struct compat_console_font_op *)op)->data); + op->flags |= KD_FONT_FLAG_OLD; + i = con_font_op(vc, op); + if (i) + return i; + ((struct compat_console_font_op *)op)->data = (unsigned long)op->data; + if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op))) + return -EFAULT; + return 0; +} + +struct compat_unimapdesc { + unsigned short entry_ct; + compat_caddr_t entries; +}; + +static inline int +compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud, + int perm, struct vc_data *vc) +{ + struct compat_unimapdesc tmp; + struct unipair __user *tmp_entries; + + if (copy_from_user(&tmp, user_ud, sizeof tmp)) + return -EFAULT; + tmp_entries = compat_ptr(tmp.entries); + if (tmp_entries) + if (!access_ok(VERIFY_WRITE, tmp_entries, + tmp.entry_ct*sizeof(struct unipair))) + return -EFAULT; + switch (cmd) { + case PIO_UNIMAP: + if (!perm) + return -EPERM; + return con_set_unimap(vc, tmp.entry_ct, tmp_entries); + case GIO_UNIMAP: + if (!perm && fg_console != vc->vc_num) + return -EPERM; + return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries); + } + return 0; +} + +long vt_compat_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct vc_data *vc = tty->driver_data; + struct console_font_op op; /* used in multiple places here */ + struct kbd_struct *kbd; + unsigned int console; + void __user *up = (void __user *)arg; + int perm; + int ret = 0; + + console = vc->vc_num; + + lock_kernel(); + + if (!vc_cons_allocated(console)) { /* impossible? */ + ret = -ENOIOCTLCMD; + goto out; + } + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. + */ + perm = 0; + if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) + perm = 1; + + kbd = kbd_table + console; + switch (cmd) { + /* + * these need special handlers for incompatible data structures + */ + case PIO_FONTX: + case GIO_FONTX: + ret = compat_fontx_ioctl(cmd, up, perm, &op); + break; + + case KDFONTOP: + ret = compat_kdfontop_ioctl(up, perm, &op, vc); + break; + + case PIO_UNIMAP: + case GIO_UNIMAP: + ret = do_unimap_ioctl(cmd, up, perm, vc); + break; + + /* + * all these treat 'arg' as an integer + */ + case KIOCSOUND: + case KDMKTONE: +#ifdef CONFIG_X86 + case KDADDIO: + case KDDELIO: +#endif + case KDSETMODE: + case KDMAPDISP: + case KDUNMAPDISP: + case KDSKBMODE: + case KDSKBMETA: + case KDSKBLED: + case KDSETLED: + case KDSIGACCEPT: + case VT_ACTIVATE: + case VT_WAITACTIVE: + case VT_RELDISP: + case VT_DISALLOCATE: + case VT_RESIZE: + case VT_RESIZEX: + goto fallback; + + /* + * the rest has a compatible data structure behind arg, + * but we have to convert it to a proper 64 bit pointer. + */ + default: + arg = (unsigned long)compat_ptr(arg); + goto fallback; + } +out: + unlock_kernel(); + return ret; + +fallback: + unlock_kernel(); + return vt_ioctl(tty, file, cmd, arg); +} + + +#endif /* CONFIG_COMPAT */ + + /* * Performs the back end of a vt switch. Called under the console * semaphore. diff --git a/include/linux/tty.h b/include/linux/tty.h index a0e03309a3798626cd5ae8702decb85b6107091d..f0f43d08d8b8765911d51f681034841a73b1c239 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -536,5 +536,8 @@ extern int pcxe_open(struct tty_struct *tty, struct file *filp); extern int vt_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg); + #endif /* __KERNEL__ */ #endif