diff options
| -rw-r--r-- | drivers/tty/vt/consolemap.c | 115 |
1 files changed, 74 insertions, 41 deletions
diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 9d7ab7b66a8a..71e81406ef71 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c | |||
| @@ -9,6 +9,17 @@ | |||
| 9 | * Support for multiple unimaps by Jakub Jelinek <jj@ultra.linux.cz>, July 1998 | 9 | * Support for multiple unimaps by Jakub Jelinek <jj@ultra.linux.cz>, July 1998 |
| 10 | * | 10 | * |
| 11 | * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998 | 11 | * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998 |
| 12 | * | ||
| 13 | * In order to prevent the following circular lock dependency: | ||
| 14 | * &mm->mmap_sem --> cpu_hotplug.lock --> console_lock --> &mm->mmap_sem | ||
| 15 | * | ||
| 16 | * We cannot allow page fault to happen while holding the console_lock. | ||
| 17 | * Therefore, all the userspace copy operations have to be done outside | ||
| 18 | * the console_lock critical sections. | ||
| 19 | * | ||
| 20 | * As all the affected functions are all called directly from vt_ioctl(), we | ||
| 21 | * can allocate some small buffers directly on stack without worrying about | ||
| 22 | * stack overflow. | ||
| 12 | */ | 23 | */ |
| 13 | 24 | ||
| 14 | #include <linux/module.h> | 25 | #include <linux/module.h> |
| @@ -22,6 +33,7 @@ | |||
| 22 | #include <linux/console.h> | 33 | #include <linux/console.h> |
| 23 | #include <linux/consolemap.h> | 34 | #include <linux/consolemap.h> |
| 24 | #include <linux/vt_kern.h> | 35 | #include <linux/vt_kern.h> |
| 36 | #include <linux/string.h> | ||
| 25 | 37 | ||
| 26 | static unsigned short translations[][256] = { | 38 | static unsigned short translations[][256] = { |
| 27 | /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ | 39 | /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ |
| @@ -309,18 +321,19 @@ static void update_user_maps(void) | |||
| 309 | int con_set_trans_old(unsigned char __user * arg) | 321 | int con_set_trans_old(unsigned char __user * arg) |
| 310 | { | 322 | { |
| 311 | int i; | 323 | int i; |
| 312 | unsigned short *p = translations[USER_MAP]; | 324 | unsigned short inbuf[E_TABSZ]; |
| 313 | 325 | ||
| 314 | if (!access_ok(VERIFY_READ, arg, E_TABSZ)) | 326 | if (!access_ok(VERIFY_READ, arg, E_TABSZ)) |
| 315 | return -EFAULT; | 327 | return -EFAULT; |
| 316 | 328 | ||
| 317 | console_lock(); | 329 | for (i = 0; i < E_TABSZ ; i++) { |
| 318 | for (i=0; i<E_TABSZ ; i++) { | ||
| 319 | unsigned char uc; | 330 | unsigned char uc; |
| 320 | __get_user(uc, arg+i); | 331 | __get_user(uc, arg+i); |
| 321 | p[i] = UNI_DIRECT_BASE | uc; | 332 | inbuf[i] = UNI_DIRECT_BASE | uc; |
| 322 | } | 333 | } |
| 323 | 334 | ||
| 335 | console_lock(); | ||
| 336 | memcpy(translations[USER_MAP], inbuf, sizeof(inbuf)); | ||
| 324 | update_user_maps(); | 337 | update_user_maps(); |
| 325 | console_unlock(); | 338 | console_unlock(); |
| 326 | return 0; | 339 | return 0; |
| @@ -330,35 +343,37 @@ int con_get_trans_old(unsigned char __user * arg) | |||
| 330 | { | 343 | { |
| 331 | int i, ch; | 344 | int i, ch; |
| 332 | unsigned short *p = translations[USER_MAP]; | 345 | unsigned short *p = translations[USER_MAP]; |
| 346 | unsigned char outbuf[E_TABSZ]; | ||
| 333 | 347 | ||
| 334 | if (!access_ok(VERIFY_WRITE, arg, E_TABSZ)) | 348 | if (!access_ok(VERIFY_WRITE, arg, E_TABSZ)) |
| 335 | return -EFAULT; | 349 | return -EFAULT; |
| 336 | 350 | ||
| 337 | console_lock(); | 351 | console_lock(); |
| 338 | for (i=0; i<E_TABSZ ; i++) | 352 | for (i = 0; i < E_TABSZ ; i++) |
| 339 | { | 353 | { |
| 340 | ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]); | 354 | ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]); |
| 341 | __put_user((ch & ~0xff) ? 0 : ch, arg+i); | 355 | outbuf[i] = (ch & ~0xff) ? 0 : ch; |
| 342 | } | 356 | } |
| 343 | console_unlock(); | 357 | console_unlock(); |
| 358 | |||
| 359 | for (i = 0; i < E_TABSZ ; i++) | ||
| 360 | __put_user(outbuf[i], arg+i); | ||
| 344 | return 0; | 361 | return 0; |
| 345 | } | 362 | } |
| 346 | 363 | ||
| 347 | int con_set_trans_new(ushort __user * arg) | 364 | int con_set_trans_new(ushort __user * arg) |
| 348 | { | 365 | { |
| 349 | int i; | 366 | int i; |
| 350 | unsigned short *p = translations[USER_MAP]; | 367 | unsigned short inbuf[E_TABSZ]; |
| 351 | 368 | ||
| 352 | if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short))) | 369 | if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short))) |
| 353 | return -EFAULT; | 370 | return -EFAULT; |
| 354 | 371 | ||
| 355 | console_lock(); | 372 | for (i = 0; i < E_TABSZ ; i++) |
| 356 | for (i=0; i<E_TABSZ ; i++) { | 373 | __get_user(inbuf[i], arg+i); |
| 357 | unsigned short us; | ||
| 358 | __get_user(us, arg+i); | ||
| 359 | p[i] = us; | ||
| 360 | } | ||
| 361 | 374 | ||
| 375 | console_lock(); | ||
| 376 | memcpy(translations[USER_MAP], inbuf, sizeof(inbuf)); | ||
| 362 | update_user_maps(); | 377 | update_user_maps(); |
| 363 | console_unlock(); | 378 | console_unlock(); |
| 364 | return 0; | 379 | return 0; |
| @@ -367,16 +382,17 @@ int con_set_trans_new(ushort __user * arg) | |||
| 367 | int con_get_trans_new(ushort __user * arg) | 382 | int con_get_trans_new(ushort __user * arg) |
| 368 | { | 383 | { |
| 369 | int i; | 384 | int i; |
| 370 | unsigned short *p = translations[USER_MAP]; | 385 | unsigned short outbuf[E_TABSZ]; |
| 371 | 386 | ||
| 372 | if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short))) | 387 | if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short))) |
| 373 | return -EFAULT; | 388 | return -EFAULT; |
| 374 | 389 | ||
| 375 | console_lock(); | 390 | console_lock(); |
| 376 | for (i=0; i<E_TABSZ ; i++) | 391 | memcpy(outbuf, translations[USER_MAP], sizeof(outbuf)); |
| 377 | __put_user(p[i], arg+i); | ||
| 378 | console_unlock(); | 392 | console_unlock(); |
| 379 | 393 | ||
| 394 | for (i = 0; i < E_TABSZ ; i++) | ||
| 395 | __put_user(outbuf[i], arg+i); | ||
| 380 | return 0; | 396 | return 0; |
| 381 | } | 397 | } |
| 382 | 398 | ||
| @@ -536,10 +552,20 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
| 536 | { | 552 | { |
| 537 | int err = 0, err1, i; | 553 | int err = 0, err1, i; |
| 538 | struct uni_pagedir *p, *q; | 554 | struct uni_pagedir *p, *q; |
| 555 | struct unipair *unilist, *plist; | ||
| 539 | 556 | ||
| 540 | if (!ct) | 557 | if (!ct) |
| 541 | return 0; | 558 | return 0; |
| 542 | 559 | ||
| 560 | unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); | ||
| 561 | if (!unilist) | ||
| 562 | return -ENOMEM; | ||
| 563 | |||
| 564 | for (i = ct, plist = unilist; i; i--, plist++, list++) { | ||
| 565 | __get_user(plist->unicode, &list->unicode); | ||
| 566 | __get_user(plist->fontpos, &list->fontpos); | ||
| 567 | } | ||
| 568 | |||
| 543 | console_lock(); | 569 | console_lock(); |
| 544 | 570 | ||
| 545 | /* Save original vc_unipagdir_loc in case we allocate a new one */ | 571 | /* Save original vc_unipagdir_loc in case we allocate a new one */ |
| @@ -557,8 +583,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
| 557 | 583 | ||
| 558 | err1 = con_do_clear_unimap(vc); | 584 | err1 = con_do_clear_unimap(vc); |
| 559 | if (err1) { | 585 | if (err1) { |
| 560 | console_unlock(); | 586 | err = err1; |
| 561 | return err1; | 587 | goto out_unlock; |
| 562 | } | 588 | } |
| 563 | 589 | ||
| 564 | /* | 590 | /* |
| @@ -592,8 +618,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
| 592 | *vc->vc_uni_pagedir_loc = p; | 618 | *vc->vc_uni_pagedir_loc = p; |
| 593 | con_release_unimap(q); | 619 | con_release_unimap(q); |
| 594 | kfree(q); | 620 | kfree(q); |
| 595 | console_unlock(); | 621 | err = err1; |
| 596 | return err1; | 622 | goto out_unlock; |
| 597 | } | 623 | } |
| 598 | } | 624 | } |
| 599 | } else { | 625 | } else { |
| @@ -617,22 +643,17 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
| 617 | /* | 643 | /* |
| 618 | * Insert user specified unicode pairs into new table. | 644 | * Insert user specified unicode pairs into new table. |
| 619 | */ | 645 | */ |
| 620 | while (ct--) { | 646 | for (plist = unilist; ct; ct--, plist++) { |
| 621 | unsigned short unicode, fontpos; | 647 | err1 = con_insert_unipair(p, plist->unicode, plist->fontpos); |
| 622 | __get_user(unicode, &list->unicode); | 648 | if (err1) |
| 623 | __get_user(fontpos, &list->fontpos); | ||
| 624 | if ((err1 = con_insert_unipair(p, unicode,fontpos)) != 0) | ||
| 625 | err = err1; | 649 | err = err1; |
| 626 | list++; | ||
| 627 | } | 650 | } |
| 628 | 651 | ||
| 629 | /* | 652 | /* |
| 630 | * Merge with fontmaps of any other virtual consoles. | 653 | * Merge with fontmaps of any other virtual consoles. |
| 631 | */ | 654 | */ |
| 632 | if (con_unify_unimap(vc, p)) { | 655 | if (con_unify_unimap(vc, p)) |
| 633 | console_unlock(); | 656 | goto out_unlock; |
| 634 | return err; | ||
| 635 | } | ||
| 636 | 657 | ||
| 637 | for (i = 0; i <= 3; i++) | 658 | for (i = 0; i <= 3; i++) |
| 638 | set_inverse_transl(vc, p, i); /* Update inverse translations */ | 659 | set_inverse_transl(vc, p, i); /* Update inverse translations */ |
| @@ -640,6 +661,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) | |||
| 640 | 661 | ||
| 641 | out_unlock: | 662 | out_unlock: |
| 642 | console_unlock(); | 663 | console_unlock(); |
| 664 | kfree(unilist); | ||
| 643 | return err; | 665 | return err; |
| 644 | } | 666 | } |
| 645 | 667 | ||
| @@ -735,9 +757,15 @@ EXPORT_SYMBOL(con_copy_unimap); | |||
| 735 | */ | 757 | */ |
| 736 | int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) | 758 | int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) |
| 737 | { | 759 | { |
| 738 | int i, j, k, ect; | 760 | int i, j, k; |
| 761 | ushort ect; | ||
| 739 | u16 **p1, *p2; | 762 | u16 **p1, *p2; |
| 740 | struct uni_pagedir *p; | 763 | struct uni_pagedir *p; |
| 764 | struct unipair *unilist, *plist; | ||
| 765 | |||
| 766 | unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); | ||
| 767 | if (!unilist) | ||
| 768 | return -ENOMEM; | ||
| 741 | 769 | ||
| 742 | console_lock(); | 770 | console_lock(); |
| 743 | 771 | ||
| @@ -750,21 +778,26 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni | |||
| 750 | for (j = 0; j < 32; j++) { | 778 | for (j = 0; j < 32; j++) { |
| 751 | p2 = *(p1++); | 779 | p2 = *(p1++); |
| 752 | if (p2) | 780 | if (p2) |
| 753 | for (k = 0; k < 64; k++) { | 781 | for (k = 0; k < 64; k++, p2++) { |
| 754 | if (*p2 < MAX_GLYPH && ect++ < ct) { | 782 | if (*p2 >= MAX_GLYPH) |
| 755 | __put_user((u_short)((i<<11)+(j<<6)+k), | 783 | continue; |
| 756 | &list->unicode); | 784 | if (ect < ct) { |
| 757 | __put_user((u_short) *p2, | 785 | unilist[ect].unicode = |
| 758 | &list->fontpos); | 786 | (i<<11)+(j<<6)+k; |
| 759 | list++; | 787 | unilist[ect].fontpos = *p2; |
| 760 | } | 788 | } |
| 761 | p2++; | 789 | ect++; |
| 762 | } | 790 | } |
| 763 | } | 791 | } |
| 764 | } | 792 | } |
| 765 | } | 793 | } |
| 766 | __put_user(ect, uct); | ||
| 767 | console_unlock(); | 794 | console_unlock(); |
| 795 | for (i = min(ect, ct), plist = unilist; i; i--, list++, plist++) { | ||
| 796 | __put_user(plist->unicode, &list->unicode); | ||
| 797 | __put_user(plist->fontpos, &list->fontpos); | ||
| 798 | } | ||
| 799 | __put_user(ect, uct); | ||
| 800 | kfree(unilist); | ||
| 768 | return ((ect <= ct) ? 0 : -ENOMEM); | 801 | return ((ect <= ct) ? 0 : -ENOMEM); |
| 769 | } | 802 | } |
| 770 | 803 | ||
