Commit f7a16fa3 authored by Alexander Richards's avatar Alexander Richards Committed by Alex Deucher
Browse files

drm/radeon: check PS, WS index

Theoretically, it would be possible for a buggy or malicious VBIOS to
overwrite past the bounds of the passed parameters (or its own
workspace); add bounds checking to prevent this from happening.

Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3093


Signed-off-by: default avatarAlexander Richards <electrodeyt@gmail.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 4630d503
Loading
Loading
Loading
Loading
+33 −14
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@
typedef struct {
	struct atom_context *ctx;
	uint32_t *ps, *ws;
	int ps_size, ws_size;
	int ps_shift;
	uint16_t start;
	unsigned last_jump;
@@ -68,8 +69,8 @@ typedef struct {
} atom_exec_context;

int atom_debug = 0;
static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params);
int atom_execute_table(struct atom_context *ctx, int index, uint32_t *params);
static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params, int params_size);
int atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size);

static uint32_t atom_arg_mask[8] = {
	0xFFFFFFFF, 0x0000FFFF, 0x00FFFF00, 0xFFFF0000,
@@ -221,7 +222,10 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
		(*ptr)++;
		/* get_unaligned_le32 avoids unaligned accesses from atombios
		 * tables, noticed on a DEC Alpha. */
		if (idx < ctx->ps_size)
			val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
		else
			pr_info("PS index out of range: %i > %i\n", idx, ctx->ps_size);
		if (print)
			DEBUG("PS[0x%02X,0x%04X]", idx, val);
		break;
@@ -259,7 +263,10 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
			val = gctx->reg_block;
			break;
		default:
			if (idx < ctx->ws_size)
				val = ctx->ws[idx];
			else
				pr_info("WS index out of range: %i > %i\n", idx, ctx->ws_size);
		}
		break;
	case ATOM_ARG_ID:
@@ -494,6 +501,10 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
		idx = U8(*ptr);
		(*ptr)++;
		DEBUG("PS[0x%02X]", idx);
		if (idx >= ctx->ps_size) {
			pr_info("PS index out of range: %i > %i\n", idx, ctx->ps_size);
			return;
		}
		ctx->ps[idx] = cpu_to_le32(val);
		break;
	case ATOM_ARG_WS:
@@ -526,6 +537,10 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
			gctx->reg_block = val;
			break;
		default:
			if (idx >= ctx->ws_size) {
				pr_info("WS index out of range: %i > %i\n", idx, ctx->ws_size);
				return;
			}
			ctx->ws[idx] = val;
		}
		break;
@@ -623,7 +638,7 @@ static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
	else
		SDEBUG("   table: %d\n", idx);
	if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
		r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
		r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift, ctx->ps_size - ctx->ps_shift);
	if (r) {
		ctx->abort = true;
	}
@@ -1152,7 +1167,7 @@ static struct {
	atom_op_shr, ATOM_ARG_MC}, {
atom_op_debug, 0},};

static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params)
static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params, int params_size)
{
	int base = CU16(ctx->cmd_table + 4 + 2 * index);
	int len, ws, ps, ptr;
@@ -1174,12 +1189,16 @@ static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32
	ectx.ps_shift = ps / 4;
	ectx.start = base;
	ectx.ps = params;
	ectx.ps_size = params_size;
	ectx.abort = false;
	ectx.last_jump = 0;
	if (ws)
	if (ws) {
		ectx.ws = kcalloc(4, ws, GFP_KERNEL);
	else
		ectx.ws_size = ws;
	} else {
		ectx.ws = NULL;
		ectx.ws_size = 0;
	}

	debug_depth++;
	while (1) {
@@ -1212,7 +1231,7 @@ static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32
	return ret;
}

int atom_execute_table_scratch_unlocked(struct atom_context *ctx, int index, uint32_t *params)
int atom_execute_table_scratch_unlocked(struct atom_context *ctx, int index, uint32_t *params, int params_size)
{
	int r;

@@ -1228,16 +1247,16 @@ int atom_execute_table_scratch_unlocked(struct atom_context *ctx, int index, uin
	/* reset divmul */
	ctx->divmul[0] = 0;
	ctx->divmul[1] = 0;
	r = atom_execute_table_locked(ctx, index, params);
	r = atom_execute_table_locked(ctx, index, params, params_size);
	mutex_unlock(&ctx->mutex);
	return r;
}

int atom_execute_table(struct atom_context *ctx, int index, uint32_t *params)
int atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size)
{
	int r;
	mutex_lock(&ctx->scratch_mutex);
	r = atom_execute_table_scratch_unlocked(ctx, index, params);
	r = atom_execute_table_scratch_unlocked(ctx, index, params, params_size);
	mutex_unlock(&ctx->scratch_mutex);
	return r;
}
@@ -1335,7 +1354,7 @@ int atom_asic_init(struct atom_context *ctx)

	if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT))
		return 1;
	ret = atom_execute_table(ctx, ATOM_CMD_INIT, ps);
	ret = atom_execute_table(ctx, ATOM_CMD_INIT, ps, 16);
	if (ret)
		return ret;

@@ -1343,7 +1362,7 @@ int atom_asic_init(struct atom_context *ctx)

	if (rdev->family < CHIP_R600) {
		if (CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_SPDFANCNTL))
			atom_execute_table(ctx, ATOM_CMD_SPDFANCNTL, ps);
			atom_execute_table(ctx, ATOM_CMD_SPDFANCNTL, ps, 16);
	}
	return ret;
}
+2 −2
Original line number Diff line number Diff line
@@ -145,8 +145,8 @@ struct atom_context {
extern int atom_debug;

struct atom_context *atom_parse(struct card_info *, void *);
int atom_execute_table(struct atom_context *, int, uint32_t *);
int atom_execute_table_scratch_unlocked(struct atom_context *, int, uint32_t *);
int atom_execute_table(struct atom_context *, int, uint32_t *, int);
int atom_execute_table_scratch_unlocked(struct atom_context *, int, uint32_t *, int);
int atom_asic_init(struct atom_context *);
void atom_destroy(struct atom_context *);
bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size,
+14 −14
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ static void atombios_overscan_setup(struct drm_crtc *crtc,
		args.usOverscanTop = cpu_to_le16(radeon_crtc->v_border);
		break;
	}
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static void atombios_scaler_setup(struct drm_crtc *crtc)
@@ -157,7 +157,7 @@ static void atombios_scaler_setup(struct drm_crtc *crtc)
			break;
		}
	}
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
	if ((is_tv || is_cv)
	    && rdev->family >= CHIP_RV515 && rdev->family <= CHIP_R580) {
		atom_rv515_force_tv_scaler(rdev, radeon_crtc);
@@ -178,7 +178,7 @@ static void atombios_lock_crtc(struct drm_crtc *crtc, int lock)
	args.ucCRTC = radeon_crtc->crtc_id;
	args.ucEnable = lock;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static void atombios_enable_crtc(struct drm_crtc *crtc, int state)
@@ -194,7 +194,7 @@ static void atombios_enable_crtc(struct drm_crtc *crtc, int state)
	args.ucCRTC = radeon_crtc->crtc_id;
	args.ucEnable = state;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static void atombios_enable_crtc_memreq(struct drm_crtc *crtc, int state)
@@ -210,7 +210,7 @@ static void atombios_enable_crtc_memreq(struct drm_crtc *crtc, int state)
	args.ucCRTC = radeon_crtc->crtc_id;
	args.ucEnable = state;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static const u32 vga_control_regs[6] =
@@ -242,7 +242,7 @@ static void atombios_blank_crtc(struct drm_crtc *crtc, int state)
	args.ucCRTC = radeon_crtc->crtc_id;
	args.ucBlanking = state;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

	if (ASIC_IS_DCE8(rdev))
		WREG32(vga_control_regs[radeon_crtc->crtc_id], vga_control);
@@ -261,7 +261,7 @@ static void atombios_powergate_crtc(struct drm_crtc *crtc, int state)
	args.ucDispPipeId = radeon_crtc->crtc_id;
	args.ucEnable = state;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
@@ -343,7 +343,7 @@ atombios_set_crtc_dtd_timing(struct drm_crtc *crtc,
	args.susModeMiscInfo.usAccess = cpu_to_le16(misc);
	args.ucCRTC = radeon_crtc->crtc_id;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static void atombios_crtc_set_timing(struct drm_crtc *crtc,
@@ -389,7 +389,7 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc,
	args.susModeMiscInfo.usAccess = cpu_to_le16(misc);
	args.ucCRTC = radeon_crtc->crtc_id;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static void atombios_disable_ss(struct radeon_device *rdev, int pll_id)
@@ -546,7 +546,7 @@ static void atombios_crtc_program_ss(struct radeon_device *rdev,
		args.lvds_ss.ucSpreadSpectrumStepSize_Delay |= (ss->delay & 7) << 4;
		args.lvds_ss.ucEnable = enable;
	}
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

union adjust_pixel_clock {
@@ -692,7 +692,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
						ADJUST_DISPLAY_CONFIG_SS_ENABLE;

				atom_execute_table(rdev->mode_info.atom_context,
						   index, (uint32_t *)&args);
						   index, (uint32_t *)&args, sizeof(args));
				adjusted_clock = le16_to_cpu(args.v1.usPixelClock) * 10;
				break;
			case 3:
@@ -725,7 +725,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
					args.v3.sInput.ucExtTransmitterID = 0;

				atom_execute_table(rdev->mode_info.atom_context,
						   index, (uint32_t *)&args);
						   index, (uint32_t *)&args, sizeof(args));
				adjusted_clock = le32_to_cpu(args.v3.sOutput.ulDispPllFreq) * 10;
				if (args.v3.sOutput.ucRefDiv) {
					radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV;
@@ -809,7 +809,7 @@ static void atombios_crtc_set_disp_eng_pll(struct radeon_device *rdev,
		DRM_ERROR("Unknown table version %d %d\n", frev, crev);
		return;
	}
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static void atombios_crtc_program_pll(struct drm_crtc *crtc,
@@ -949,7 +949,7 @@ static void atombios_crtc_program_pll(struct drm_crtc *crtc,
		return;
	}

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static bool atombios_crtc_prepare_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+2 −2
Original line number Diff line number Diff line
@@ -112,7 +112,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
	if (ASIC_IS_DCE4(rdev))
		args.v2.ucHPD_ID = chan->rec.hpd;

	atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

	*ack = args.v1.ucReplyStatus;

@@ -354,7 +354,7 @@ static u8 radeon_dp_encoder_service(struct radeon_device *rdev,
	args.ucLaneNum = lane_num;
	args.ucStatus = 0;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
	return args.ucStatus;
}

+19 −19
Original line number Diff line number Diff line
@@ -119,12 +119,12 @@ atombios_set_backlight_level(struct radeon_encoder *radeon_encoder, u8 level)
			index = GetIndexIntoMasterTable(COMMAND, LCD1OutputControl);
			if (dig->backlight_level == 0) {
				args.ucAction = ATOM_LCD_BLOFF;
				atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
				atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
			} else {
				args.ucAction = ATOM_LCD_BL_BRIGHTNESS_CONTROL;
				atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
				atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
				args.ucAction = ATOM_LCD_BLON;
				atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
				atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
			}
			break;
		case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
@@ -389,7 +389,7 @@ atombios_dac_setup(struct drm_encoder *encoder, int action)
	}
	args.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

}

@@ -445,7 +445,7 @@ atombios_tv_setup(struct drm_encoder *encoder, int action)

	args.sTVEncoder.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

}

@@ -546,7 +546,7 @@ atombios_dvo_setup(struct drm_encoder *encoder, int action)
		break;
	}

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

union lvds_encoder_control {
@@ -664,7 +664,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
		break;
	}

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

int
@@ -979,7 +979,7 @@ atombios_dig_encoder_setup2(struct drm_encoder *encoder, int action, int panel_m
		break;
	}

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

}

@@ -1361,7 +1361,7 @@ atombios_dig_transmitter_setup2(struct drm_encoder *encoder, int action, uint8_t
		break;
	}

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

void
@@ -1397,7 +1397,7 @@ atombios_set_edp_panel_power(struct drm_connector *connector, int action)

	args.v1.ucAction = action;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

	/* wait for the panel to power up */
	if (action == ATOM_TRANSMITTER_ACTION_POWER_ON) {
@@ -1519,7 +1519,7 @@ atombios_external_encoder_setup(struct drm_encoder *encoder,
		DRM_ERROR("Unknown table version: %d, %d\n", frev, crev);
		return;
	}
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

static void
@@ -1554,7 +1554,7 @@ atombios_yuv_setup(struct drm_encoder *encoder, bool enable)
		args.ucEnable = ATOM_ENABLE;
	args.ucCRTC = radeon_crtc->crtc_id;

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

	WREG32(reg, temp);
}
@@ -1618,10 +1618,10 @@ radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode)
		if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_DDI) {
			u32 reg = RREG32(RADEON_BIOS_3_SCRATCH);
			WREG32(RADEON_BIOS_3_SCRATCH, reg & ~ATOM_S3_DFP2I_ACTIVE);
			atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
			atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
			WREG32(RADEON_BIOS_3_SCRATCH, reg);
		} else
			atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
			atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
		if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
			if (rdev->mode_info.bl_encoder) {
				struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
@@ -1629,7 +1629,7 @@ radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode)
				atombios_set_backlight_level(radeon_encoder, dig->backlight_level);
			} else {
				args.ucAction = ATOM_LCD_BLON;
				atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
				atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
			}
		}
		break;
@@ -1637,10 +1637,10 @@ radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode)
	case DRM_MODE_DPMS_SUSPEND:
	case DRM_MODE_DPMS_OFF:
		args.ucAction = ATOM_DISABLE;
		atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
		atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
		if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
			args.ucAction = ATOM_LCD_BLOFF;
			atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
			atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
		}
		break;
	}
@@ -1983,7 +1983,7 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
		return;
	}

	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

	/* update scratch regs with new routing */
	radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
@@ -2311,7 +2311,7 @@ atombios_dac_load_detect(struct drm_encoder *encoder, struct drm_connector *conn
				args.sDacload.ucMisc = DAC_LOAD_MISC_YPrPb;
		}

		atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
		atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));

		return true;
	} else
Loading