]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/amd/display: Bound VBIOS record-chain walk loops
authorHarry Wentland <harry.wentland@amd.com>
Tue, 12 May 2026 19:24:22 +0000 (15:24 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 3 Jun 2026 18:45:44 +0000 (14:45 -0400)
[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 <alex.hung@amd.com>
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Ray Wu <ray.wu@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
(cherry picked from commit 95700a3d660287ed657d6892f7be9ffc0e294a93)
Cc: stable@vger.kernel.org
drivers/gpu/drm/amd/display/dc/bios/bios_parser.c
drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h

index c307f42fe0b988d5d7190bc92f6e9e543f40583f..507b628abdb5b3b98c1e5bef691f5c8578b30a23 100644 (file)
@@ -222,6 +222,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;
@@ -234,7 +235,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)
@@ -293,11 +294,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)
@@ -966,6 +968,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 */
@@ -975,7 +978,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)
@@ -1670,6 +1673,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 */
@@ -1679,7 +1683,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)
@@ -2769,6 +2773,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb,
 {
        (void)i;
        unsigned int j;
+       unsigned int n;
        struct bios_parser *bp;
        ATOM_BRACKET_LAYOUT_RECORD *record;
        ATOM_COMMON_RECORD_HEADER *record_header;
@@ -2778,7 +2783,7 @@ static enum bp_result update_slot_layout_info(struct dc_bios *dcb,
        record = NULL;
        record_header = NULL;
 
-       for (;;) {
+       for (n = 0; n < BIOS_MAX_NUM_RECORD; n++) {
 
                record_header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset);
                if (record_header == NULL) {
index 4f213ea865b8ad54ef6332e4a4512a424074f953..0e1f973326ed9b9b81d1f91b34f7f66d68ac7ed4 100644 (file)
@@ -396,6 +396,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;
@@ -429,7 +430,7 @@ static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb,
                break;
        }
 
-       for (;;) {
+       for (i = 0; i < BIOS_MAX_NUM_RECORD; i++) {
                header = GET_IMAGE(struct atom_common_record_header, offset);
 
                if (!header)
@@ -534,6 +535,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser
 {
        struct atom_common_record_header *header;
        uint32_t offset;
+       int i;
 
        if (!object) {
                BREAK_TO_DEBUGGER(); /* Invalid object */
@@ -542,7 +544,7 @@ static struct atom_hpd_int_record *get_hpd_record_for_path_v3(struct bios_parser
 
        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)
@@ -611,6 +613,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 */
@@ -620,7 +623,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)
@@ -2195,6 +2198,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 */
@@ -2203,7 +2207,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)
@@ -2232,6 +2236,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 */
@@ -2240,7 +2245,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)
@@ -2268,6 +2273,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_
 {
        struct atom_common_record_header *header;
        uint32_t offset;
+       int i;
 
        if (!object) {
                BREAK_TO_DEBUGGER(); /* Invalid object */
@@ -2276,7 +2282,7 @@ static struct atom_connector_caps_record *get_connector_caps_record(struct bios_
 
        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)
@@ -2354,6 +2360,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct
 {
        struct atom_common_record_header *header;
        uint32_t offset;
+       int i;
 
        if (!object) {
                BREAK_TO_DEBUGGER(); /* Invalid object */
@@ -2362,7 +2369,7 @@ static struct atom_connector_speed_record *get_connector_speed_cap_record(struct
 
        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)
@@ -3263,6 +3270,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;
@@ -3284,7 +3292,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,
@@ -3378,6 +3386,7 @@ static enum bp_result update_slot_layout_info_v2(
        struct slot_layout_info *slot_layout_info)
 {
        unsigned int record_offset;
+       unsigned int n;
        struct atom_display_object_path_v3 *object;
        struct atom_bracket_layout_record_v2 *record;
        struct atom_common_record_header *record_header;
@@ -3400,7 +3409,7 @@ static enum bp_result update_slot_layout_info_v2(
                (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,
index ab162f2fe57766fa08ec6412b6567ccc208c7b8c..19fd7aea18f11ebebe8b058aa0c64afecabe6b34 100644 (file)
@@ -37,4 +37,9 @@ void bios_set_scratch_critical_state(struct dc_bios *bios, bool state);
 
 #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