Re: arm64: fp-stress: BUG: KFENCE: memory corruption in fpsimd_release_task

From: Dan Carpenter
Date: Wed May 17 2023 - 15:31:03 EST


I don't know this code at all so probably this is dumb... I don't
undestand how vec_set_vector_length() ensures that sme_state_size()
stays in sync with the actual size allocated in sme_alloc()

arch/arm64/kernel/fpsimd.c
847 int vec_set_vector_length(struct task_struct *task, enum vec_type type,
848 unsigned long vl, unsigned long flags)
^^^
"vl" comes from the user and is 0-u16max.

849 {
850 if (flags & ~(unsigned long)(PR_SVE_VL_INHERIT |
851 PR_SVE_SET_VL_ONEXEC))
852 return -EINVAL;
853
854 if (!sve_vl_valid(vl))

valid values are '16-8192'

855 return -EINVAL;
856
857 /*
858 * Clamp to the maximum vector length that VL-agnostic code
859 * can work with. A flag may be assigned in the future to
860 * allow setting of larger vector lengths without confusing
861 * older software.
862 */
863 if (vl > VL_ARCH_MAX)
864 vl = VL_ARCH_MAX;

Now 16-256'

865
866 vl = find_supported_vector_length(type, vl);

type is ARM64_VEC_SVE. I've looked at this function for a while and
I don't see anything which ensures that "vl" is less than the current
value.

867
868 if (flags & (PR_SVE_VL_INHERIT |
869 PR_SVE_SET_VL_ONEXEC))
870 task_set_vl_onexec(task, type, vl);
871 else
872 /* Reset VL to system default on next exec: */
873 task_set_vl_onexec(task, type, 0);
874
875 /* Only actually set the VL if not deferred: */
876 if (flags & PR_SVE_SET_VL_ONEXEC)

Assume the PR_SVE_SET_VL_ONEXEC flag is not set.

877 goto out;
878
879 if (vl == task_get_vl(task, type))

This checks if the flag is == and if so we are done.

880 goto out;
881
882 /*
883 * To ensure the FPSIMD bits of the SVE vector registers are preserved,
884 * write any live register state back to task_struct, and convert to a
885 * regular FPSIMD thread.
886 */
887 if (task == current) {
888 get_cpu_fpsimd_context();
889
890 fpsimd_save();
891 }
892
893 fpsimd_flush_task_state(task);
894 if (test_and_clear_tsk_thread_flag(task, TIF_SVE) ||
895 thread_sm_enabled(&task->thread)) {
896 sve_to_fpsimd(task);
897 task->thread.fp_type = FP_STATE_FPSIMD;
898 }
899
900 if (system_supports_sme() && type == ARM64_VEC_SME) {
901 task->thread.svcr &= ~(SVCR_SM_MASK |
902 SVCR_ZA_MASK);
903 clear_thread_flag(TIF_SME);
904 }
905
906 if (task == current)
907 put_cpu_fpsimd_context();
908
909 /*
910 * Force reallocation of task SVE and SME state to the correct
911 * size on next use:
912 */
913 sve_free(task);
914 if (system_supports_sme() && type == ARM64_VEC_SME)
915 sme_free(task);
916
917 task_set_vl(task, type, vl);

"vl" is set here. This is fine if we are setting it to a smaller value,
but if we are setting it to a larger value then I think we need to
realloc the ->sme_state buffer.

When we call sme_alloc() it will say the buffer is already allocated
and just zero out what we need for "vl", but the existing buffer is too
small.

918
919 out:
920 update_tsk_thread_flag(task, vec_vl_inherit_flag(type),
922
923 return 0;
924 }

regards,
dan carpenter