]> 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 17:38:54 +0000 (13:38 -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>
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 91df37c4c8da3121843b788922c94752188f8c57..fb30a3e5dfdb002b9eb05e320b046dd32a579a77 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)
@@ -2733,6 +2737,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;
@@ -2742,7 +2747,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 6f99cfc28e795199a99d14957a81bc052639d4b5..9764c1a478c54d3f581a5ac64d12e569782b4470 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)
@@ -3246,6 +3253,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;
@@ -3267,7 +3275,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,
@@ -3361,6 +3369,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;
@@ -3383,7 +3392,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