Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

metag/usercopy: Add early abort to copy_to_user

When copying to userland on Meta, if any faults are encountered
immediately abort the copy instead of continuing on and repeatedly
faulting, and worse potentially copying further bytes successfully to
subsequent valid pages.

Fixes: 373cd784d0fc ("metag: Memory handling")
Reported-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: linux-metag@vger.kernel.org
Cc: stable@vger.kernel.org

+20
+20
arch/metag/lib/usercopy.c
··· 538 538 if ((unsigned long) src & 1) { 539 539 __asm_copy_to_user_1(dst, src, retn); 540 540 n--; 541 + if (retn) 542 + return retn + n; 541 543 } 542 544 if ((unsigned long) dst & 1) { 543 545 /* Worst case - byte copy */ 544 546 while (n > 0) { 545 547 __asm_copy_to_user_1(dst, src, retn); 546 548 n--; 549 + if (retn) 550 + return retn + n; 547 551 } 548 552 } 549 553 if (((unsigned long) src & 2) && n >= 2) { 550 554 __asm_copy_to_user_2(dst, src, retn); 551 555 n -= 2; 556 + if (retn) 557 + return retn + n; 552 558 } 553 559 if ((unsigned long) dst & 2) { 554 560 /* Second worst case - word copy */ 555 561 while (n >= 2) { 556 562 __asm_copy_to_user_2(dst, src, retn); 557 563 n -= 2; 564 + if (retn) 565 + return retn + n; 558 566 } 559 567 } 560 568 ··· 577 569 while (n >= 8) { 578 570 __asm_copy_to_user_8x64(dst, src, retn); 579 571 n -= 8; 572 + if (retn) 573 + return retn + n; 580 574 } 581 575 } 582 576 if (n >= RAPF_MIN_BUF_SIZE) { ··· 591 581 while (n >= 8) { 592 582 __asm_copy_to_user_8x64(dst, src, retn); 593 583 n -= 8; 584 + if (retn) 585 + return retn + n; 594 586 } 595 587 } 596 588 #endif ··· 600 588 while (n >= 16) { 601 589 __asm_copy_to_user_16(dst, src, retn); 602 590 n -= 16; 591 + if (retn) 592 + return retn + n; 603 593 } 604 594 605 595 while (n >= 4) { 606 596 __asm_copy_to_user_4(dst, src, retn); 607 597 n -= 4; 598 + if (retn) 599 + return retn + n; 608 600 } 609 601 610 602 switch (n) { ··· 625 609 break; 626 610 } 627 611 612 + /* 613 + * If we get here, retn correctly reflects the number of failing 614 + * bytes. 615 + */ 628 616 return retn; 629 617 } 630 618 EXPORT_SYMBOL(__copy_user);