Received: by 2002:a05:6a10:1287:0:0:0:0 with SMTP id d7csp3853856pxv; Mon, 19 Jul 2021 10:18:10 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxoflxHLCdo5kYbcgqgJhBFRb1u6YmkWnIhUrUz8Mi6juD7kO++3PtADes0ybY6JDEZ+Ya4 X-Received: by 2002:a92:d28b:: with SMTP id p11mr18389152ilp.250.1626715090530; Mon, 19 Jul 2021 10:18:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1626715090; cv=none; d=google.com; s=arc-20160816; b=RN61oc4mIVK2n49oHdURsg+ATxNO0g9fRL1c/sXrmE9HffGg1qJIRC73r1RgyavokY Pj4pCAIarzwJIIBPqsvkueQgHGzX+iIUaXZWYlUGCqwmF/v71I/nbXEYJxHP0vfNZKcg U/QZd6NDNqlSaq9u6aiAr7XF6bL+NEqPnO6lJfHa7lcBLbBV+d7wfz3WFMpGwvciwDRs /YOl17BUl1MCkyerW6O6EDYsj3xavKj2hZ/99R+NzWgmDBz1100sRGJoP4VVEHAlhRsM HPSUBZf9J40RXzkZ5Y29E6eShQRgt7s4UeVbjoXj+pD61lFBTzQ5NEpyEtjcLngQlt6Q tYcQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=7/QbKICcRqd8vRgG/Aya9oLLlMUL2G72PMMP3byY2FA=; b=IyaFAEHDaPuUiAWoCK9rngxsxpJ+jspU+yYUitW+cBqJOPpP5DjzfQTO5cXhQJjSZt VAtu/RwI8wLXbUzP4xZPyHTEh/c+JNtwd8FOoqvYKpjCCTAGRt+4qw0Ny8Z/hFCGC5Rr Gpgl9CfJY+oWTf3Ofio4XFZx9MUI09sV52y8+HB1S7/MHn6EhW3jSazAaYOKaxAr8VCI 2FB6rRY01ykOjVbRWlC22v1h8OsKzfKps7g4bRahwjdRo/v8LL7Rvz5yUxAtEM51XAWv Zej38vOthEkcbS2/iEc5PhBVClQJyB2Pocxnw13hp/7F+ZV9n/KLi9rX3+srCXQpg+Qt LrJg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=Yh4PUDjT; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id t9si22273207jaj.20.2021.07.19.10.17.58; Mon, 19 Jul 2021 10:18:10 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@linuxfoundation.org header.s=korg header.b=Yh4PUDjT; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linuxfoundation.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1352896AbhGSQed (ORCPT + 99 others); Mon, 19 Jul 2021 12:34:33 -0400 Received: from mail.kernel.org ([198.145.29.99]:58058 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347591AbhGSPT4 (ORCPT ); Mon, 19 Jul 2021 11:19:56 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id BE83C613F8; Mon, 19 Jul 2021 15:58:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=linuxfoundation.org; s=korg; t=1626710326; bh=uwDMjewfI8k3UJwX37rZyzWvABNcpfVjpQmK6E2ahG8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Yh4PUDjTFUd882jI/TM5MHdEnhKR/jclLdMW0veYVIgoEtTJD6h9ewCAJAthHkl+j ZgjbZV23KCCclYZEoTX4+Ek99nKeDCxd2rxKGeuR25YTNzLcvG/iK2b/ZjGHzbTTFo YTf++VmZPddn29YicZ5/PKHs6ED5FwkoflMxXZ34= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Thomas Gleixner , Borislav Petkov , Sasha Levin Subject: [PATCH 5.10 169/243] x86/fpu: Fix copy_xstate_to_kernel() gap handling Date: Mon, 19 Jul 2021 16:53:18 +0200 Message-Id: <20210719144946.363350195@linuxfoundation.org> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210719144940.904087935@linuxfoundation.org> References: <20210719144940.904087935@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Thomas Gleixner [ Upstream commit 9625895011d130033d1bc7aac0d77a9bf68ff8a6 ] The gap handling in copy_xstate_to_kernel() is wrong when XSAVES is in use. Using init_fpstate for copying the init state of features which are not set in the xstate header is only correct for the legacy area, but not for the extended features area because when XSAVES is in use then init_fpstate is in compacted form which means the xstate offsets which are used to copy from init_fpstate are not valid. Fortunately, this is not a real problem today because all extended features in use have an all-zeros init state, but it is wrong nevertheless and with a potentially dynamically sized init_fpstate this would result in an access outside of the init_fpstate. Fix this by keeping track of the last copied state in the target buffer and explicitly zero it when there is a feature or alignment gap. Use the compacted offset when accessing the extended feature space in init_fpstate. As this is not a functional issue on older kernels this is intentionally not tagged for stable. Fixes: b8be15d58806 ("x86/fpu/xstate: Re-enable XSAVES") Signed-off-by: Thomas Gleixner Signed-off-by: Borislav Petkov Reviewed-by: Borislav Petkov Link: https://lkml.kernel.org/r/20210623121451.294282032@linutronix.de Signed-off-by: Sasha Levin --- arch/x86/kernel/fpu/xstate.c | 105 ++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 44 deletions(-) diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index 80dcf0417f30..80836b94189e 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -1084,20 +1084,10 @@ static inline bool xfeatures_mxcsr_quirk(u64 xfeatures) return true; } -static void fill_gap(struct membuf *to, unsigned *last, unsigned offset) +static void copy_feature(bool from_xstate, struct membuf *to, void *xstate, + void *init_xstate, unsigned int size) { - if (*last >= offset) - return; - membuf_write(to, (void *)&init_fpstate.xsave + *last, offset - *last); - *last = offset; -} - -static void copy_part(struct membuf *to, unsigned *last, unsigned offset, - unsigned size, void *from) -{ - fill_gap(to, last, offset); - membuf_write(to, from, size); - *last = offset + size; + membuf_write(to, from_xstate ? xstate : init_xstate, size); } /* @@ -1109,10 +1099,10 @@ static void copy_part(struct membuf *to, unsigned *last, unsigned offset, */ void copy_xstate_to_kernel(struct membuf to, struct xregs_state *xsave) { + const unsigned int off_mxcsr = offsetof(struct fxregs_state, mxcsr); + struct xregs_state *xinit = &init_fpstate.xsave; struct xstate_header header; - const unsigned off_mxcsr = offsetof(struct fxregs_state, mxcsr); - unsigned size = to.left; - unsigned last = 0; + unsigned int zerofrom; int i; /* @@ -1122,41 +1112,68 @@ void copy_xstate_to_kernel(struct membuf to, struct xregs_state *xsave) header.xfeatures = xsave->header.xfeatures; header.xfeatures &= xfeatures_mask_user(); - if (header.xfeatures & XFEATURE_MASK_FP) - copy_part(&to, &last, 0, off_mxcsr, &xsave->i387); - if (header.xfeatures & (XFEATURE_MASK_SSE | XFEATURE_MASK_YMM)) - copy_part(&to, &last, off_mxcsr, - MXCSR_AND_FLAGS_SIZE, &xsave->i387.mxcsr); - if (header.xfeatures & XFEATURE_MASK_FP) - copy_part(&to, &last, offsetof(struct fxregs_state, st_space), - 128, &xsave->i387.st_space); - if (header.xfeatures & XFEATURE_MASK_SSE) - copy_part(&to, &last, xstate_offsets[XFEATURE_SSE], - 256, &xsave->i387.xmm_space); - /* - * Fill xsave->i387.sw_reserved value for ptrace frame: - */ - copy_part(&to, &last, offsetof(struct fxregs_state, sw_reserved), - 48, xstate_fx_sw_bytes); - /* - * Copy xregs_state->header: - */ - copy_part(&to, &last, offsetof(struct xregs_state, header), - sizeof(header), &header); + /* Copy FP state up to MXCSR */ + copy_feature(header.xfeatures & XFEATURE_MASK_FP, &to, &xsave->i387, + &xinit->i387, off_mxcsr); + + /* Copy MXCSR when SSE or YMM are set in the feature mask */ + copy_feature(header.xfeatures & (XFEATURE_MASK_SSE | XFEATURE_MASK_YMM), + &to, &xsave->i387.mxcsr, &xinit->i387.mxcsr, + MXCSR_AND_FLAGS_SIZE); + + /* Copy the remaining FP state */ + copy_feature(header.xfeatures & XFEATURE_MASK_FP, + &to, &xsave->i387.st_space, &xinit->i387.st_space, + sizeof(xsave->i387.st_space)); + + /* Copy the SSE state - shared with YMM, but independently managed */ + copy_feature(header.xfeatures & XFEATURE_MASK_SSE, + &to, &xsave->i387.xmm_space, &xinit->i387.xmm_space, + sizeof(xsave->i387.xmm_space)); + + /* Zero the padding area */ + membuf_zero(&to, sizeof(xsave->i387.padding)); + + /* Copy xsave->i387.sw_reserved */ + membuf_write(&to, xstate_fx_sw_bytes, sizeof(xsave->i387.sw_reserved)); + + /* Copy the user space relevant state of @xsave->header */ + membuf_write(&to, &header, sizeof(header)); + + zerofrom = offsetof(struct xregs_state, extended_state_area); for (i = FIRST_EXTENDED_XFEATURE; i < XFEATURE_MAX; i++) { /* - * Copy only in-use xstates: + * The ptrace buffer is in non-compacted XSAVE format. + * In non-compacted format disabled features still occupy + * state space, but there is no state to copy from in the + * compacted init_fpstate. The gap tracking will zero this + * later. */ - if ((header.xfeatures >> i) & 1) { - void *src = __raw_xsave_addr(xsave, i); + if (!(xfeatures_mask_user() & BIT_ULL(i))) + continue; - copy_part(&to, &last, xstate_offsets[i], - xstate_sizes[i], src); - } + /* + * If there was a feature or alignment gap, zero the space + * in the destination buffer. + */ + if (zerofrom < xstate_offsets[i]) + membuf_zero(&to, xstate_offsets[i] - zerofrom); + + copy_feature(header.xfeatures & BIT_ULL(i), &to, + __raw_xsave_addr(xsave, i), + __raw_xsave_addr(xinit, i), + xstate_sizes[i]); + /* + * Keep track of the last copied state in the non-compacted + * target buffer for gap zeroing. + */ + zerofrom = xstate_offsets[i] + xstate_sizes[i]; } - fill_gap(&to, &last, size); + + if (to.left) + membuf_zero(&to, to.left); } /* -- 2.30.2