From nobody Mon Feb 9 03:31:17 2026 Received: from mail-108-mta231.mxroute.com (mail-108-mta231.mxroute.com [136.175.108.231]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7C04F34FF77 for ; Sat, 31 Jan 2026 17:26:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=136.175.108.231 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769880380; cv=none; b=VL0OZpG+gG1J9vc224bZeH7xUWo4Dkb5wf4QututMLqqu1jDFZdOHhiA4yC5w51+Y8tUhJ3m1S5sTtUzONnN8yZAy29CW/HZ/Ex2sjDjYCUMJrxwQeJpm3CHgwAdTcbdvBRAB9DJtfJDk+9fUCNKcD94VjVthBcUZrYKZoKCvMc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769880380; c=relaxed/simple; bh=/UcYtkosgj4n5Gg6EpxseX1xNymBYn5wyYfz3Td+ZLQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NtFiR5MSd5WlmdKGtdibtC5KcmbFOEiY0NXfUrPvwC0bI5fW7obi8Bzz9bANmycL7vswr511GLRlMXbH6PxztGZVxfg28e2Y5nLuHHSyAbafWuw/JA/qARY85TCwVcPVljuHDsaUYh79zUWpiI1obpaLFamuIRNbI4Dqkfz9LpQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ry.rs; spf=pass smtp.mailfrom=ry.rs; dkim=pass (2048-bit key) header.d=ry.rs header.i=@ry.rs header.b=J3QTneX6; arc=none smtp.client-ip=136.175.108.231 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=ry.rs Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ry.rs Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ry.rs header.i=@ry.rs header.b="J3QTneX6" Received: from filter006.mxroute.com ([136.175.111.3] filter006.mxroute.com) (Authenticated sender: mN4UYu2MZsgR) by mail-108-mta231.mxroute.com (ZoneMTA) with ESMTPSA id 19c1512a2380009140.009 for (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384); Sat, 31 Jan 2026 17:21:08 +0000 X-Zone-Loop: 4e062863473c161cc2bff3b4ba084a574af516b029d8 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=ry.rs; s=x; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Date:Subject: Cc:To:From:Sender:Reply-To:Content-Type:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID; bh=ieqzaWRiWdY02r7jkJvGUOeJVM3Ht/4jcBZxw4sl63k=; b=J3QTneX6znShPblBSOCVVL4eL2 XQ+fzMub+SG/nGMQBZsYaR6EBtdqfIfWMeQd6Km6uv8m2dBUVAomm1eXprFqCqwEeWbphDb5As+HS 4rOYw2ZpE3mB0Wu8SQWvFmmrLy5lfOpm3SSERabfiDXDO8rExlGI9zkpeRVP6LAXLfZdtUQjfwglK 7NIp9BNHnGwTwaj/7yzksSdd+FPfVj13a/Rn9Jl3CEJht9I/Vq1IqcWTz+0j0jY6AAsS1ky2PAI/c w+AJ/A0B3Q54sbAY06y6Ocs0cgRAm5BuKetT7e4xder2elYEcuyGtDdKsSTm6vY9oLPTfBCSgH85U MrlyGwbQ==; From: Zijing Zhang To: dakr@kernel.org, acourbot@nvidia.com Cc: aliceryhl@google.com, airlied@gmail.com, simona@ffwll.ch, nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, zijing.zhang@ry.rs, kernel test robot Subject: [PATCH v2] gpu: nova-core: vbios: harden falcon pointer parsing Date: Sat, 31 Jan 2026 17:20:56 +0000 Message-ID: <20260131172056.1592923-1-zijing.zhang@ry.rs> In-Reply-To: <20260131111619.1360414-1-zijing.zhang@ry.rs> References: <20260131111619.1360414-1-zijing.zhang@ry.rs> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Authenticated-Id: zijing.zhang@ry.rs Content-Type: text/plain; charset="utf-8" Use checked arithmetic and slice get() to avoid overflow/underflow and out-= of-bounds access when parsing Falcon data pointers and FWSEC payloads. This also clarifies the error message for pointers that still fall within t= he PciAt image. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202601311630.eoGwXUug-lkp@int= el.com/ Signed-off-by: Zijing Zhang --- drivers/gpu/nova-core/vbios.rs | 93 ++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs index 72cba8659..2db481f45 100644 --- a/drivers/gpu/nova-core/vbios.rs +++ b/drivers/gpu/nova-core/vbios.rs @@ -784,22 +784,31 @@ fn get_bit_token(&self, token_id: u8) -> Result { fn falcon_data_ptr(&self) -> Result { let token =3D self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?; =20 - // Make sure we don't go out of bounds - if usize::from(token.data_offset) + 4 > self.base.data.len() { - return Err(EINVAL); - } - - // read the 4 bytes at the offset specified in the token let offset =3D usize::from(token.data_offset); - let bytes: [u8; 4] =3D self.base.data[offset..offset + 4].try_into= ().map_err(|_| { - dev_err!(self.base.dev, "Failed to convert data slice to array= \n"); - EINVAL - })?; + let end =3D offset.checked_add(4).ok_or(EINVAL)?; + + // Read the 4 bytes at the offset specified in the token. + let bytes: [u8; 4] =3D self + .base + .data + .get(offset..end) + .ok_or(EINVAL)? + .try_into() + .map_err(|_| { + dev_err!(self.base.dev, "Failed to convert data slice to a= rray\n"); + EINVAL + })?; =20 let data_ptr =3D u32::from_le_bytes(bytes); =20 - if (usize::from_safe_cast(data_ptr)) < self.base.data.len() { - dev_err!(self.base.dev, "Falcon data pointer out of bounds\n"); + // The BIT Falcon data pointer is expected to point outside the Pc= iAt image (into a later + // image in the ROM chain). Reject pointers that still fall within= this PciAt image, since + // downstream code will subtract `self.base.data.len()` from this = offset. + if usize::from_safe_cast(data_ptr) < self.base.data.len() { + dev_err!( + self.base.dev, + "Falcon data pointer points inside PciAt image\n" + ); return Err(EINVAL); } =20 @@ -920,14 +929,17 @@ fn setup_falcon_data( pci_at_image: &PciAtBiosImage, first_fwsec: &FwSecBiosBuilder, ) -> Result { - let mut offset =3D usize::from_safe_cast(pci_at_image.falcon_data_= ptr()?); + let data_ptr =3D pci_at_image.falcon_data_ptr()?; + let mut offset =3D usize::from_safe_cast(data_ptr); let mut pmu_in_first_fwsec =3D false; =20 // The falcon data pointer assumes that the PciAt and FWSEC images // are contiguous in memory. However, testing shows the EFI image = sits in // between them. So calculate the offset from the end of the PciAt= image // rather than the start of it. Compensate. - offset -=3D pci_at_image.base.data.len(); + offset =3D offset + .checked_sub(pci_at_image.base.data.len()) + .ok_or(EINVAL)?; =20 // The offset is now from the start of the first Fwsec image, howe= ver // the offset points to a location in the second Fwsec image. Since @@ -937,7 +949,9 @@ fn setup_falcon_data( if offset < first_fwsec.base.data.len() { pmu_in_first_fwsec =3D true; } else { - offset -=3D first_fwsec.base.data.len(); + offset =3D offset + .checked_sub(first_fwsec.base.data.len()) + .ok_or(EINVAL)?; } =20 self.falcon_data_offset =3D Some(offset); @@ -945,12 +959,16 @@ fn setup_falcon_data( if pmu_in_first_fwsec { self.pmu_lookup_table =3D Some(PmuLookupTable::new( &self.base.dev, - &first_fwsec.base.data[offset..], + first_fwsec.base.data.get(offset..).ok_or(EINVAL)?, )?); } else { + if offset >=3D self.base.data.len() { + dev_err!(self.base.dev, "Falcon data pointer out of bounds= \n"); + return Err(EINVAL); + } self.pmu_lookup_table =3D Some(PmuLookupTable::new( &self.base.dev, - &self.base.data[offset..], + self.base.data.get(offset..).ok_or(EINVAL)?, )?); } =20 @@ -962,12 +980,20 @@ fn setup_falcon_data( { Ok(entry) =3D> { let mut ucode_offset =3D usize::from_safe_cast(entry.data); - ucode_offset -=3D pci_at_image.base.data.len(); + ucode_offset =3D ucode_offset + .checked_sub(pci_at_image.base.data.len()) + .ok_or(EINVAL)?; if ucode_offset < first_fwsec.base.data.len() { dev_err!(self.base.dev, "Falcon Ucode offset not in se= cond Fwsec.\n"); return Err(EINVAL); } - ucode_offset -=3D first_fwsec.base.data.len(); + ucode_offset =3D ucode_offset + .checked_sub(first_fwsec.base.data.len()) + .ok_or(EINVAL)?; + if ucode_offset >=3D self.base.data.len() { + dev_err!(self.base.dev, "Falcon Ucode offset out of bo= unds.\n"); + return Err(EINVAL); + } self.falcon_ucode_offset =3D Some(ucode_offset); } Err(e) =3D> { @@ -1006,7 +1032,11 @@ pub(crate) fn header(&self) -> Result { let falcon_ucode_offset =3D self.falcon_ucode_offset; =20 // Read the first 4 bytes to get the version. - let hdr_bytes: [u8; 4] =3D self.base.data[falcon_ucode_offset..fal= con_ucode_offset + 4] + let hdr_bytes: [u8; 4] =3D self + .base + .data + .get(falcon_ucode_offset..falcon_ucode_offset.checked_add(4).o= k_or(EINVAL)?) + .ok_or(EINVAL)? .try_into() .map_err(|_| EINVAL)?; let hdr =3D u32::from_le_bytes(hdr_bytes); @@ -1038,13 +1068,18 @@ pub(crate) fn ucode(&self, desc: &FalconUCodeDesc) = -> Result<&[u8]> { let falcon_ucode_offset =3D self.falcon_ucode_offset; =20 // The ucode data follows the descriptor. - let ucode_data_offset =3D falcon_ucode_offset + desc.size(); - let size =3D usize::from_safe_cast(desc.imem_load_size() + desc.dm= em_load_size()); + let ucode_data_offset =3D falcon_ucode_offset.checked_add(desc.siz= e()).ok_or(ERANGE)?; + let size_u32 =3D desc + .imem_load_size() + .checked_add(desc.dmem_load_size()) + .ok_or(ERANGE)?; + let size =3D usize::from_safe_cast(size_u32); + let ucode_data_end =3D ucode_data_offset.checked_add(size).ok_or(E= RANGE)?; =20 // Get the data slice, checking bounds in a single operation. self.base .data - .get(ucode_data_offset..ucode_data_offset + size) + .get(ucode_data_offset..ucode_data_end) .ok_or(ERANGE) .inspect_err(|_| { dev_err!( @@ -1061,12 +1096,18 @@ pub(crate) fn sigs(&self, desc: &FalconUCodeDesc) -= > Result<&[Bcrt30Rsa3kSignatu FalconUCodeDesc::V3(_v3) =3D> core::mem::size_of::(), }; // The signatures data follows the descriptor. - let sigs_data_offset =3D self.falcon_ucode_offset + hdr_size; + let sigs_data_offset =3D self + .falcon_ucode_offset + .checked_add(hdr_size) + .ok_or(ERANGE)?; let sigs_count =3D usize::from(desc.signature_count()); - let sigs_size =3D sigs_count * core::mem::size_of::(); + let sigs_size =3D sigs_count + .checked_mul(core::mem::size_of::()) + .ok_or(ERANGE)?; =20 // Make sure the data is within bounds. - if sigs_data_offset + sigs_size > self.base.data.len() { + let end =3D sigs_data_offset.checked_add(sigs_size).ok_or(ERANGE)?; + if end > self.base.data.len() { dev_err!( self.base.dev, "fwsec signatures data not contained within BIOS bounds\n" --=20 2.52.0