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

misc: sgi-gru: fix use-after-free error in gru_set_context_option, gru_fault and gru_handle_user_call_os

In some bad situation, the gts may be freed gru_check_chiplet_assignment.
The call chain can be gru_unload_context->gru_free_gru_context->gts_drop
and kfree finally. However, the caller didn't know if the gts is freed
or not and use it afterwards. This will trigger a Use after Free bug.

Fix it by introducing a return value to see if it's in error path or not.
Free the gts in caller if gru_check_chiplet_assignment check failed.

Fixes: 55484c45dbec ("gru: allow users to specify gru chiplet 2")
Signed-off-by: Zheng Wang <zyytlz.wz@163.com>
Acked-by: Dimitri Sivanich <sivanich@hpe.com>
Link: https://lore.kernel.org/r/20221110035033.19498-1-zyytlz.wz@163.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Zheng Wang and committed by
Greg Kroah-Hartman
643a16a0 fd2c930c

+30 -7
+11 -2
drivers/misc/sgi-gru/grufault.c
··· 648 648 if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB) 649 649 return -EINVAL; 650 650 651 + again: 651 652 gts = gru_find_lock_gts(cb); 652 653 if (!gts) 653 654 return -EINVAL; ··· 657 656 if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) 658 657 goto exit; 659 658 660 - gru_check_context_placement(gts); 659 + if (gru_check_context_placement(gts)) { 660 + gru_unlock_gts(gts); 661 + gru_unload_context(gts, 1); 662 + goto again; 663 + } 661 664 662 665 /* 663 666 * CCH may contain stale data if ts_force_cch_reload is set. ··· 879 874 } else { 880 875 gts->ts_user_blade_id = req.val1; 881 876 gts->ts_user_chiplet_id = req.val0; 882 - gru_check_context_placement(gts); 877 + if (gru_check_context_placement(gts)) { 878 + gru_unlock_gts(gts); 879 + gru_unload_context(gts, 1); 880 + return ret; 881 + } 883 882 } 884 883 break; 885 884 case sco_gseg_owner:
+18 -4
drivers/misc/sgi-gru/grumain.c
··· 716 716 * chiplet. Misassignment can occur if the process migrates to a different 717 717 * blade or if the user changes the selected blade/chiplet. 718 718 */ 719 - void gru_check_context_placement(struct gru_thread_state *gts) 719 + int gru_check_context_placement(struct gru_thread_state *gts) 720 720 { 721 721 struct gru_state *gru; 722 + int ret = 0; 722 723 723 724 /* 724 725 * If the current task is the context owner, verify that the ··· 727 726 * references. Pthread apps use non-owner references to the CBRs. 728 727 */ 729 728 gru = gts->ts_gru; 729 + /* 730 + * If gru or gts->ts_tgid_owner isn't initialized properly, return 731 + * success to indicate that the caller does not need to unload the 732 + * gru context.The caller is responsible for their inspection and 733 + * reinitialization if needed. 734 + */ 730 735 if (!gru || gts->ts_tgid_owner != current->tgid) 731 - return; 736 + return ret; 732 737 733 738 if (!gru_check_chiplet_assignment(gru, gts)) { 734 739 STAT(check_context_unload); 735 - gru_unload_context(gts, 1); 740 + ret = -EINVAL; 736 741 } else if (gru_retarget_intr(gts)) { 737 742 STAT(check_context_retarget_intr); 738 743 } 744 + 745 + return ret; 739 746 } 740 747 741 748 ··· 943 934 mutex_lock(&gts->ts_ctxlock); 944 935 preempt_disable(); 945 936 946 - gru_check_context_placement(gts); 937 + if (gru_check_context_placement(gts)) { 938 + preempt_enable(); 939 + mutex_unlock(&gts->ts_ctxlock); 940 + gru_unload_context(gts, 1); 941 + return VM_FAULT_NOPAGE; 942 + } 947 943 948 944 if (!gts->ts_gru) { 949 945 STAT(load_user_context);
+1 -1
drivers/misc/sgi-gru/grutables.h
··· 632 632 extern int gru_user_unload_context(unsigned long arg); 633 633 extern int gru_get_exception_detail(unsigned long arg); 634 634 extern int gru_set_context_option(unsigned long address); 635 - extern void gru_check_context_placement(struct gru_thread_state *gts); 635 + extern int gru_check_context_placement(struct gru_thread_state *gts); 636 636 extern int gru_cpu_fault_map_id(void); 637 637 extern struct vm_area_struct *gru_find_vma(unsigned long vaddr); 638 638 extern void gru_flush_all_tlb(struct gru_state *gru);