LCOV - code coverage report
Current view: top level - drivers/tty/vt - selection.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 2 188 1.1 %
Date: 2021-04-22 12:43:58 Functions: 1 12 8.3 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * This module exports the functions:
       4             :  *
       5             :  *     'int set_selection_user(struct tiocl_selection __user *,
       6             :  *                             struct tty_struct *)'
       7             :  *     'int set_selection_kernel(struct tiocl_selection *, struct tty_struct *)'
       8             :  *     'void clear_selection(void)'
       9             :  *     'int paste_selection(struct tty_struct *)'
      10             :  *     'int sel_loadlut(char __user *)'
      11             :  *
      12             :  * Now that /dev/vcs exists, most of this can disappear again.
      13             :  */
      14             : 
      15             : #include <linux/module.h>
      16             : #include <linux/tty.h>
      17             : #include <linux/sched.h>
      18             : #include <linux/mm.h>
      19             : #include <linux/mutex.h>
      20             : #include <linux/slab.h>
      21             : #include <linux/types.h>
      22             : 
      23             : #include <linux/uaccess.h>
      24             : 
      25             : #include <linux/kbd_kern.h>
      26             : #include <linux/vt_kern.h>
      27             : #include <linux/consolemap.h>
      28             : #include <linux/selection.h>
      29             : #include <linux/tiocl.h>
      30             : #include <linux/console.h>
      31             : #include <linux/tty_flip.h>
      32             : 
      33             : #include <linux/sched/signal.h>
      34             : 
      35             : /* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
      36             : #define isspace(c)      ((c) == ' ')
      37             : 
      38             : /* FIXME: all this needs locking */
      39             : static struct vc_selection {
      40             :         struct mutex lock;
      41             :         struct vc_data *cons;                   /* must not be deallocated */
      42             :         char *buffer;
      43             :         unsigned int buf_len;
      44             :         volatile int start;                     /* cleared by clear_selection */
      45             :         int end;
      46             : } vc_sel = {
      47             :         .lock = __MUTEX_INITIALIZER(vc_sel.lock),
      48             :         .start = -1,
      49             : };
      50             : 
      51             : /* clear_selection, highlight and highlight_pointer can be called
      52             :    from interrupt (via scrollback/front) */
      53             : 
      54             : /* set reverse video on characters s-e of console with selection. */
      55           0 : static inline void highlight(const int s, const int e)
      56             : {
      57           0 :         invert_screen(vc_sel.cons, s, e-s+2, true);
      58           0 : }
      59             : 
      60             : /* use complementary color to show the pointer */
      61           0 : static inline void highlight_pointer(const int where)
      62             : {
      63           0 :         complement_pos(vc_sel.cons, where);
      64             : }
      65             : 
      66             : static u32
      67           0 : sel_pos(int n, bool unicode)
      68             : {
      69           0 :         if (unicode)
      70           0 :                 return screen_glyph_unicode(vc_sel.cons, n / 2);
      71           0 :         return inverse_translate(vc_sel.cons, screen_glyph(vc_sel.cons, n), 0);
      72             : }
      73             : 
      74             : /**
      75             :  *      clear_selection         -       remove current selection
      76             :  *
      77             :  *      Remove the current selection highlight, if any from the console
      78             :  *      holding the selection. The caller must hold the console lock.
      79             :  */
      80           0 : void clear_selection(void)
      81             : {
      82           0 :         highlight_pointer(-1); /* hide the pointer */
      83           0 :         if (vc_sel.start != -1) {
      84           0 :                 highlight(vc_sel.start, vc_sel.end);
      85           0 :                 vc_sel.start = -1;
      86             :         }
      87           0 : }
      88             : EXPORT_SYMBOL_GPL(clear_selection);
      89             : 
      90          33 : bool vc_is_sel(struct vc_data *vc)
      91             : {
      92          33 :         return vc == vc_sel.cons;
      93             : }
      94             : 
      95             : /*
      96             :  * User settable table: what characters are to be considered alphabetic?
      97             :  * 128 bits. Locked by the console lock.
      98             :  */
      99             : static u32 inwordLut[]={
     100             :   0x00000000, /* control chars     */
     101             :   0x03FFE000, /* digits and "-./"  */
     102             :   0x87FFFFFE, /* uppercase and '_' */
     103             :   0x07FFFFFE, /* lowercase         */
     104             : };
     105             : 
     106           0 : static inline int inword(const u32 c)
     107             : {
     108           0 :         return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
     109             : }
     110             : 
     111             : /**
     112             :  *      set loadlut             -       load the LUT table
     113             :  *      @p: user table
     114             :  *
     115             :  *      Load the LUT table from user space. The caller must hold the console
     116             :  *      lock. Make a temporary copy so a partial update doesn't make a mess.
     117             :  */
     118           0 : int sel_loadlut(char __user *p)
     119             : {
     120           0 :         u32 tmplut[ARRAY_SIZE(inwordLut)];
     121           0 :         if (copy_from_user(tmplut, (u32 __user *)(p+4), sizeof(inwordLut)))
     122             :                 return -EFAULT;
     123           0 :         memcpy(inwordLut, tmplut, sizeof(inwordLut));
     124           0 :         return 0;
     125             : }
     126             : 
     127             : /* does screen address p correspond to character at LH/RH edge of screen? */
     128           0 : static inline int atedge(const int p, int size_row)
     129             : {
     130           0 :         return (!(p % size_row) || !((p + 2) % size_row));
     131             : }
     132             : 
     133             : /* stores the char in UTF8 and returns the number of bytes used (1-4) */
     134           0 : static int store_utf8(u32 c, char *p)
     135             : {
     136           0 :         if (c < 0x80) {
     137             :                 /*  0******* */
     138           0 :                 p[0] = c;
     139           0 :                 return 1;
     140           0 :         } else if (c < 0x800) {
     141             :                 /* 110***** 10****** */
     142           0 :                 p[0] = 0xc0 | (c >> 6);
     143           0 :                 p[1] = 0x80 | (c & 0x3f);
     144           0 :                 return 2;
     145           0 :         } else if (c < 0x10000) {
     146             :                 /* 1110**** 10****** 10****** */
     147           0 :                 p[0] = 0xe0 | (c >> 12);
     148           0 :                 p[1] = 0x80 | ((c >> 6) & 0x3f);
     149           0 :                 p[2] = 0x80 | (c & 0x3f);
     150           0 :                 return 3;
     151           0 :         } else if (c < 0x110000) {
     152             :                 /* 11110*** 10****** 10****** 10****** */
     153           0 :                 p[0] = 0xf0 | (c >> 18);
     154           0 :                 p[1] = 0x80 | ((c >> 12) & 0x3f);
     155           0 :                 p[2] = 0x80 | ((c >> 6) & 0x3f);
     156           0 :                 p[3] = 0x80 | (c & 0x3f);
     157           0 :                 return 4;
     158             :         } else {
     159             :                 /* outside Unicode, replace with U+FFFD */
     160           0 :                 p[0] = 0xef;
     161           0 :                 p[1] = 0xbf;
     162           0 :                 p[2] = 0xbd;
     163           0 :                 return 3;
     164             :         }
     165             : }
     166             : 
     167             : /**
     168             :  *      set_selection_user      -       set the current selection.
     169             :  *      @sel: user selection info
     170             :  *      @tty: the console tty
     171             :  *
     172             :  *      Invoked by the ioctl handle for the vt layer.
     173             :  *
     174             :  *      The entire selection process is managed under the console_lock. It's
     175             :  *       a lot under the lock but its hardly a performance path
     176             :  */
     177           0 : int set_selection_user(const struct tiocl_selection __user *sel,
     178             :                        struct tty_struct *tty)
     179             : {
     180           0 :         struct tiocl_selection v;
     181             : 
     182           0 :         if (copy_from_user(&v, sel, sizeof(*sel)))
     183             :                 return -EFAULT;
     184             : 
     185           0 :         return set_selection_kernel(&v, tty);
     186             : }
     187             : 
     188           0 : static int vc_selection_store_chars(struct vc_data *vc, bool unicode)
     189             : {
     190           0 :         char *bp, *obp;
     191           0 :         unsigned int i;
     192             : 
     193             :         /* Allocate a new buffer before freeing the old one ... */
     194             :         /* chars can take up to 4 bytes with unicode */
     195           0 :         bp = kmalloc_array((vc_sel.end - vc_sel.start) / 2 + 1, unicode ? 4 : 1,
     196             :                            GFP_KERNEL | __GFP_NOWARN);
     197           0 :         if (!bp) {
     198           0 :                 printk(KERN_WARNING "selection: kmalloc() failed\n");
     199           0 :                 clear_selection();
     200           0 :                 return -ENOMEM;
     201             :         }
     202           0 :         kfree(vc_sel.buffer);
     203           0 :         vc_sel.buffer = bp;
     204             : 
     205           0 :         obp = bp;
     206           0 :         for (i = vc_sel.start; i <= vc_sel.end; i += 2) {
     207           0 :                 u32 c = sel_pos(i, unicode);
     208           0 :                 if (unicode)
     209           0 :                         bp += store_utf8(c, bp);
     210             :                 else
     211           0 :                         *bp++ = c;
     212           0 :                 if (!isspace(c))
     213           0 :                         obp = bp;
     214           0 :                 if (!((i + 2) % vc->vc_size_row)) {
     215             :                         /* strip trailing blanks from line and add newline,
     216             :                            unless non-space at end of line. */
     217           0 :                         if (obp != bp) {
     218           0 :                                 bp = obp;
     219           0 :                                 *bp++ = '\r';
     220             :                         }
     221             :                         obp = bp;
     222             :                 }
     223             :         }
     224           0 :         vc_sel.buf_len = bp - vc_sel.buffer;
     225             : 
     226           0 :         return 0;
     227             : }
     228             : 
     229           0 : static int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps,
     230             :                 int pe)
     231             : {
     232           0 :         int new_sel_start, new_sel_end, spc;
     233           0 :         bool unicode = vt_do_kdgkbmode(fg_console) == K_UNICODE;
     234             : 
     235           0 :         switch (mode) {
     236             :         case TIOCL_SELCHAR:     /* character-by-character selection */
     237             :                 new_sel_start = ps;
     238             :                 new_sel_end = pe;
     239             :                 break;
     240           0 :         case TIOCL_SELWORD:     /* word-by-word selection */
     241           0 :                 spc = isspace(sel_pos(ps, unicode));
     242           0 :                 for (new_sel_start = ps; ; ps -= 2) {
     243           0 :                         if ((spc && !isspace(sel_pos(ps, unicode))) ||
     244           0 :                             (!spc && !inword(sel_pos(ps, unicode))))
     245             :                                 break;
     246           0 :                         new_sel_start = ps;
     247           0 :                         if (!(ps % vc->vc_size_row))
     248             :                                 break;
     249             :                 }
     250             : 
     251           0 :                 spc = isspace(sel_pos(pe, unicode));
     252           0 :                 for (new_sel_end = pe; ; pe += 2) {
     253           0 :                         if ((spc && !isspace(sel_pos(pe, unicode))) ||
     254           0 :                             (!spc && !inword(sel_pos(pe, unicode))))
     255             :                                 break;
     256           0 :                         new_sel_end = pe;
     257           0 :                         if (!((pe + 2) % vc->vc_size_row))
     258             :                                 break;
     259             :                 }
     260             :                 break;
     261           0 :         case TIOCL_SELLINE:     /* line-by-line selection */
     262           0 :                 new_sel_start = rounddown(ps, vc->vc_size_row);
     263           0 :                 new_sel_end = rounddown(pe, vc->vc_size_row) +
     264           0 :                         vc->vc_size_row - 2;
     265           0 :                 break;
     266             :         case TIOCL_SELPOINTER:
     267           0 :                 highlight_pointer(pe);
     268           0 :                 return 0;
     269             :         default:
     270             :                 return -EINVAL;
     271             :         }
     272             : 
     273             :         /* remove the pointer */
     274           0 :         highlight_pointer(-1);
     275             : 
     276             :         /* select to end of line if on trailing space */
     277           0 :         if (new_sel_end > new_sel_start &&
     278           0 :                 !atedge(new_sel_end, vc->vc_size_row) &&
     279           0 :                 isspace(sel_pos(new_sel_end, unicode))) {
     280             :                 for (pe = new_sel_end + 2; ; pe += 2)
     281           0 :                         if (!isspace(sel_pos(pe, unicode)) ||
     282           0 :                             atedge(pe, vc->vc_size_row))
     283             :                                 break;
     284           0 :                 if (isspace(sel_pos(pe, unicode)))
     285           0 :                         new_sel_end = pe;
     286             :         }
     287           0 :         if (vc_sel.start == -1) /* no current selection */
     288           0 :                 highlight(new_sel_start, new_sel_end);
     289           0 :         else if (new_sel_start == vc_sel.start)
     290             :         {
     291           0 :                 if (new_sel_end == vc_sel.end)  /* no action required */
     292             :                         return 0;
     293           0 :                 else if (new_sel_end > vc_sel.end)   /* extend to right */
     294           0 :                         highlight(vc_sel.end + 2, new_sel_end);
     295             :                 else                            /* contract from right */
     296           0 :                         highlight(new_sel_end + 2, vc_sel.end);
     297             :         }
     298           0 :         else if (new_sel_end == vc_sel.end)
     299             :         {
     300           0 :                 if (new_sel_start < vc_sel.start) /* extend to left */
     301           0 :                         highlight(new_sel_start, vc_sel.start - 2);
     302             :                 else                            /* contract from left */
     303           0 :                         highlight(vc_sel.start, new_sel_start - 2);
     304             :         }
     305             :         else    /* some other case; start selection from scratch */
     306             :         {
     307           0 :                 clear_selection();
     308           0 :                 highlight(new_sel_start, new_sel_end);
     309             :         }
     310           0 :         vc_sel.start = new_sel_start;
     311           0 :         vc_sel.end = new_sel_end;
     312             : 
     313           0 :         return vc_selection_store_chars(vc, unicode);
     314             : }
     315             : 
     316           0 : static int vc_selection(struct vc_data *vc, struct tiocl_selection *v,
     317             :                 struct tty_struct *tty)
     318             : {
     319           0 :         int ps, pe;
     320             : 
     321           0 :         poke_blanked_console();
     322             : 
     323           0 :         if (v->sel_mode == TIOCL_SELCLEAR) {
     324             :                 /* useful for screendump without selection highlights */
     325           0 :                 clear_selection();
     326           0 :                 return 0;
     327             :         }
     328             : 
     329           0 :         v->xs = min_t(u16, v->xs - 1, vc->vc_cols - 1);
     330           0 :         v->ys = min_t(u16, v->ys - 1, vc->vc_rows - 1);
     331           0 :         v->xe = min_t(u16, v->xe - 1, vc->vc_cols - 1);
     332           0 :         v->ye = min_t(u16, v->ye - 1, vc->vc_rows - 1);
     333             : 
     334           0 :         if (mouse_reporting() && (v->sel_mode & TIOCL_SELMOUSEREPORT)) {
     335           0 :                 mouse_report(tty, v->sel_mode & TIOCL_SELBUTTONMASK, v->xs,
     336           0 :                              v->ys);
     337           0 :                 return 0;
     338             :         }
     339             : 
     340           0 :         ps = v->ys * vc->vc_size_row + (v->xs << 1);
     341           0 :         pe = v->ye * vc->vc_size_row + (v->xe << 1);
     342           0 :         if (ps > pe) /* make vc_sel.start <= vc_sel.end */
     343           0 :                 swap(ps, pe);
     344             : 
     345           0 :         if (vc_sel.cons != vc) {
     346           0 :                 clear_selection();
     347           0 :                 vc_sel.cons = vc;
     348             :         }
     349             : 
     350           0 :         return vc_do_selection(vc, v->sel_mode, ps, pe);
     351             : }
     352             : 
     353           0 : int set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
     354             : {
     355           0 :         int ret;
     356             : 
     357           0 :         mutex_lock(&vc_sel.lock);
     358           0 :         console_lock();
     359           0 :         ret = vc_selection(vc_cons[fg_console].d, v, tty);
     360           0 :         console_unlock();
     361           0 :         mutex_unlock(&vc_sel.lock);
     362             : 
     363           0 :         return ret;
     364             : }
     365             : EXPORT_SYMBOL_GPL(set_selection_kernel);
     366             : 
     367             : /* Insert the contents of the selection buffer into the
     368             :  * queue of the tty associated with the current console.
     369             :  * Invoked by ioctl().
     370             :  *
     371             :  * Locking: called without locks. Calls the ldisc wrongly with
     372             :  * unsafe methods,
     373             :  */
     374           0 : int paste_selection(struct tty_struct *tty)
     375             : {
     376           0 :         struct vc_data *vc = tty->driver_data;
     377           0 :         int     pasted = 0;
     378           0 :         unsigned int count;
     379           0 :         struct  tty_ldisc *ld;
     380           0 :         DECLARE_WAITQUEUE(wait, current);
     381           0 :         int ret = 0;
     382             : 
     383           0 :         console_lock();
     384           0 :         poke_blanked_console();
     385           0 :         console_unlock();
     386             : 
     387           0 :         ld = tty_ldisc_ref_wait(tty);
     388           0 :         if (!ld)
     389             :                 return -EIO;    /* ldisc was hung up */
     390           0 :         tty_buffer_lock_exclusive(&vc->port);
     391             : 
     392           0 :         add_wait_queue(&vc->paste_wait, &wait);
     393           0 :         mutex_lock(&vc_sel.lock);
     394           0 :         while (vc_sel.buffer && vc_sel.buf_len > pasted) {
     395           0 :                 set_current_state(TASK_INTERRUPTIBLE);
     396           0 :                 if (signal_pending(current)) {
     397             :                         ret = -EINTR;
     398             :                         break;
     399             :                 }
     400           0 :                 if (tty_throttled(tty)) {
     401           0 :                         mutex_unlock(&vc_sel.lock);
     402           0 :                         schedule();
     403           0 :                         mutex_lock(&vc_sel.lock);
     404           0 :                         continue;
     405             :                 }
     406           0 :                 __set_current_state(TASK_RUNNING);
     407           0 :                 count = vc_sel.buf_len - pasted;
     408           0 :                 count = tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted, NULL,
     409             :                                               count);
     410           0 :                 pasted += count;
     411             :         }
     412           0 :         mutex_unlock(&vc_sel.lock);
     413           0 :         remove_wait_queue(&vc->paste_wait, &wait);
     414           0 :         __set_current_state(TASK_RUNNING);
     415             : 
     416           0 :         tty_buffer_unlock_exclusive(&vc->port);
     417           0 :         tty_ldisc_deref(ld);
     418           0 :         return ret;
     419             : }
     420             : EXPORT_SYMBOL_GPL(paste_selection);

Generated by: LCOV version 1.14