From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 988DA3F44F0 for ; Thu, 18 Jun 2026 13:44:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781790249; cv=none; b=r+ug5CClwTnSD3aQ37QmUjoBVcew1oDBYya2pY3xA1R8jGH9qJjlYVP82yhm4knr3TVV2GGHF7uOoe2rkj6+qN1O2S5lnzPbJ3h08VRp7SD/6mI6PzEkGgJCjiCAr+0pDW0LwZRBznK/wfmdANvm0i7J+cibgNGU6fqCWikBTwI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781790249; c=relaxed/simple; bh=aLz7O+mc9LdIauqZCWkGlFDwdP2UaWb1aanyQtvhk5k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=P3qSbuTl2DoB8yJM0A/0nG0swxCtk45CZhSjMYxFRllOkOhXoR5g/ZGTUZRU4OKOrtoPMXv+XryR+snUUDvLHXIIfg/QEK6YNS1xjADoFxMtUz0dyOd0nhBCtyoqEt+UWypffvr6YyRaCnzDogPSwfHdddyWBRuXqIhCggqipZs= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DxB5rgC7; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DxB5rgC7" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 625951F000E9; Thu, 18 Jun 2026 13:44:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781790246; bh=r8va8Y2LNRUICnv9+liqtdzU/mMniMEuPHjFhKGsEk8=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=DxB5rgC7Uot4dU5WjOs00dfbknLmEsn9RIBiAkfGemK/EHh6t+POhT9yhN1HJINR1 MXUws1CY2gixg7eIadFHMmG4enWKW261ZBi3LRDfHw+TiuM86b7petC/7AJwhR7qlQ HNuPOUY55p2qVLMRP+ssw0mdtJNG8YBYfNQsYUzMh/IQZnQBBbYwdAs3c1oH6owIub XpnOtHV8lAwqkgS38qL0nGAcscf8Az4cMTF7sIfwh7jlWn74uqdm1wTVHP3csG52n0 eIn65PQHXsBHFWwwH28Wwl7o3I7gmMp0Zik4lDaXbkQsc5VrzoJKvdWZhl/THNfnOX Pn9059qMScTbA== From: Sasha Levin To: stable@vger.kernel.org Cc: Harry Wentland , Alex Hung , Ray Wu , Daniel Wheeler , Alex Deucher , Sasha Levin Subject: [PATCH 5.15.y] drm/amd/display: Bound VBIOS record-chain walk loops Date: Thu, 18 Jun 2026 09:44:02 -0400 Message-ID: <20260618134402.720671-1-sashal@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <2026061556-breeze-customs-7a2e@gregkh> References: <2026061556-breeze-customs-7a2e@gregkh> Precedence: bulk X-Mailing-List: stable@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Harry Wentland [ Upstream commit ff287df16a1a58aca78b08d1f3ee09fc44da0351 ] [Why & How] All record-chain walk loops in bios_parser.c and bios_parser2.c use for(;;) and only terminate on a 0xFF record_type sentinel or zero record_size. A malformed VBIOS image missing the terminator record causes unbounded iteration at probe time, potentially hundreds of thousands of iterations with record_size=1. In the final iterations near the BIOS image boundary, struct casts beyond the 2-byte header validated by GET_IMAGE can also read out of bounds. Cap all 14 record-chain walk loops to BIOS_MAX_NUM_RECORD (256) iterations. The atombios.h defines up to 22 distinct record types and atomfirmware.h has 13. Assuming an average of less than 10 records per type (which is reasonable since most are connector- based) 256 is a generous upper bound. Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") Assisted-by: Copilot:claude-opus-4.6 Mythos Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ray Wu Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 95700a3d660287ed657d6892f7be9ffc0e294a93) Cc: stable@vger.kernel.org Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/display/dc/bios/bios_parser.c | 15 ++++++++++----- .../gpu/drm/amd/display/dc/bios/bios_parser2.c | 15 ++++++++++----- .../drm/amd/display/dc/bios/bios_parser_helper.h | 5 +++++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index d8982aca8ef680..d2730af8e8030f 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -223,6 +223,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, ATOM_COMMON_RECORD_HEADER *header; ATOM_I2C_RECORD *record; struct bios_parser *bp = BP_FROM_DCB(dcb); + int i; if (!info) return BP_RESULT_BADINPUT; @@ -235,7 +236,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -294,11 +295,12 @@ static enum bp_result bios_parser_get_device_tag_record( { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -870,6 +872,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -879,7 +882,7 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -1572,6 +1575,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( { ATOM_COMMON_RECORD_HEADER *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -1581,7 +1585,7 @@ static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( offset = le16_to_cpu(object->usRecordOffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); if (!header) @@ -2665,6 +2669,7 @@ static enum bp_result update_slot_layout_info( unsigned int record_offset) { unsigned int j; + unsigned int n; struct bios_parser *bp; ATOM_BRACKET_LAYOUT_RECORD *record; ATOM_COMMON_RECORD_HEADER *record_header; @@ -2674,7 +2679,7 @@ static enum bp_result update_slot_layout_info( record = NULL; record_header = NULL; - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = (ATOM_COMMON_RECORD_HEADER *) GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index bebdc9b53bb6ba..0b9c475b186b07 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -296,6 +296,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, struct atom_i2c_record *record; struct atom_i2c_record dummy_record = {0}; struct bios_parser *bp = BP_FROM_DCB(dcb); + int i; if (!info) return BP_RESULT_BADINPUT; @@ -316,7 +317,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -446,6 +447,7 @@ static struct atom_hpd_int_record *get_hpd_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -455,7 +457,7 @@ static struct atom_hpd_int_record *get_hpd_record( offset = le16_to_cpu(object->disp_recordoffset) + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -1612,6 +1614,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -1620,7 +1623,7 @@ static struct atom_encoder_caps_record *get_encoder_cap_record( offset = object->encoder_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -1649,6 +1652,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( { struct atom_common_record_header *header; uint32_t offset; + int i; if (!object) { BREAK_TO_DEBUGGER(); /* Invalid object */ @@ -1657,7 +1661,7 @@ static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( offset = object->disp_recordoffset + bp->object_info_tbl_offset; - for (;;) { + for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) { header = GET_IMAGE(struct atom_common_record_header, offset); if (!header) @@ -2402,6 +2406,7 @@ static enum bp_result update_slot_layout_info( { unsigned int record_offset; unsigned int j; + unsigned int n; struct atom_display_object_path_v2 *object; struct atom_bracket_layout_record *record; struct atom_common_record_header *record_header; @@ -2423,7 +2428,7 @@ static enum bp_result update_slot_layout_info( (object->disp_recordoffset) + (unsigned int)(bp->object_info_tbl_offset); - for (;;) { + for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) { record_header = (struct atom_common_record_header *) GET_IMAGE(struct atom_common_record_header, diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h index e1b4a40a353db1..da1e30de3c59a0 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h @@ -38,4 +38,9 @@ uint32_t bios_get_vga_enabled_displays(struct dc_bios *bios); #define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) +/* Upper bound on the number of records in a VBIOS record chain. Prevents + * unbounded looping if the VBIOS image is malformed and lacks a terminator. + */ +#define BIOS_MAX_NUM_RECORD 256 + #endif -- 2.53.0