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

drm/amdgpu: 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 a25dea47
Loading
Loading
Loading
Loading
+16 −8
Original line number Diff line number Diff line
@@ -1018,7 +1018,8 @@ int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev,
		if (clock_type == COMPUTE_ENGINE_PLL_PARAM) {
			args.v3.ulClockParams = cpu_to_le32((clock_type << 24) | clock);

			amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
			amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args,
				sizeof(args));

			dividers->post_div = args.v3.ucPostDiv;
			dividers->enable_post_div = (args.v3.ucCntlFlag &
@@ -1038,7 +1039,8 @@ int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev,
			if (strobe_mode)
				args.v5.ucInputFlag = ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN;

			amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
			amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args,
				sizeof(args));

			dividers->post_div = args.v5.ucPostDiv;
			dividers->enable_post_div = (args.v5.ucCntlFlag &
@@ -1056,7 +1058,8 @@ int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev,
		/* fusion */
		args.v4.ulClock = cpu_to_le32(clock);	/* 10 khz */

		amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
		amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args,
			sizeof(args));

		dividers->post_divider = dividers->post_div = args.v4.ucPostDiv;
		dividers->real_clock = le32_to_cpu(args.v4.ulClock);
@@ -1067,7 +1070,8 @@ int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev,
		args.v6_in.ulClock.ulComputeClockFlag = clock_type;
		args.v6_in.ulClock.ulClockFreq = cpu_to_le32(clock);	/* 10 khz */

		amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
		amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args,
			sizeof(args));

		dividers->whole_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDiv);
		dividers->frac_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDivFrac);
@@ -1109,7 +1113,8 @@ int amdgpu_atombios_get_memory_pll_dividers(struct amdgpu_device *adev,
			if (strobe_mode)
				args.ucInputFlag |= MPLL_INPUT_FLAG_STROBE_MODE_EN;

			amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
			amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args,
				sizeof(args));

			mpll_param->clkfrac = le16_to_cpu(args.ulFbDiv.usFbDivFrac);
			mpll_param->clkf = le16_to_cpu(args.ulFbDiv.usFbDiv);
@@ -1151,7 +1156,8 @@ void amdgpu_atombios_set_engine_dram_timings(struct amdgpu_device *adev,
	if (mem_clock)
		args.sReserved.ulClock = cpu_to_le32(mem_clock & SET_CLOCK_FREQ_MASK);

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

void amdgpu_atombios_get_default_voltages(struct amdgpu_device *adev,
@@ -1205,7 +1211,8 @@ int amdgpu_atombios_get_max_vddc(struct amdgpu_device *adev, u8 voltage_type,
		args.v2.ucVoltageMode = 0;
		args.v2.usVoltageLevel = 0;

		amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
		amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args,
			sizeof(args));

		*voltage = le16_to_cpu(args.v2.usVoltageLevel);
		break;
@@ -1214,7 +1221,8 @@ int amdgpu_atombios_get_max_vddc(struct amdgpu_device *adev, u8 voltage_type,
		args.v3.ucVoltageMode = ATOM_GET_VOLTAGE_LEVEL;
		args.v3.usVoltageLevel = cpu_to_le16(voltage_id);

		amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
		amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args,
			sizeof(args));

		*voltage = le16_to_cpu(args.v3.usVoltageLevel);
		break;
+2 −1
Original line number Diff line number Diff line
@@ -941,5 +941,6 @@ int amdgpu_atomfirmware_asic_init(struct amdgpu_device *adev, bool fb_reset)
		return -EINVAL;
	}

	return amdgpu_atom_execute_table(ctx, ATOM_CMD_INIT, (uint32_t *)&asic_init_ps_v2_1);
	return amdgpu_atom_execute_table(ctx, ATOM_CMD_INIT, (uint32_t *)&asic_init_ps_v2_1,
		sizeof(asic_init_ps_v2_1));
}
+30 −11
Original line number Diff line number Diff line
@@ -62,6 +62,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;
@@ -70,8 +71,8 @@ typedef struct {
} atom_exec_context;

int amdgpu_atom_debug;
static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params);
int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params);
static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params, int params_size);
int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size);

static uint32_t atom_arg_mask[8] =
	{ 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000,
@@ -223,7 +224,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;
@@ -261,7 +265,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:
@@ -495,6 +502,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:
@@ -527,6 +538,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;
@@ -624,7 +639,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 = amdgpu_atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
		r = amdgpu_atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift, ctx->ps_size - ctx->ps_shift);
	if (r) {
		ctx->abort = true;
	}
@@ -1203,7 +1218,7 @@ static struct {
	atom_op_div32, ATOM_ARG_WS},
};

static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params)
static int amdgpu_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;
@@ -1225,12 +1240,16 @@ static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index,
	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) {
@@ -1264,7 +1283,7 @@ static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index,
	return ret;
}

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

@@ -1280,7 +1299,7 @@ int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *par
	/* reset divmul */
	ctx->divmul[0] = 0;
	ctx->divmul[1] = 0;
	r = amdgpu_atom_execute_table_locked(ctx, index, params);
	r = amdgpu_atom_execute_table_locked(ctx, index, params, params_size);
	mutex_unlock(&ctx->mutex);
	return r;
}
@@ -1552,7 +1571,7 @@ int amdgpu_atom_asic_init(struct atom_context *ctx)

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

+1 −1
Original line number Diff line number Diff line
@@ -156,7 +156,7 @@ struct atom_context {
extern int amdgpu_atom_debug;

struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios);
int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params);
int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size);
int amdgpu_atom_asic_init(struct atom_context *ctx);
void amdgpu_atom_destroy(struct atom_context *ctx);
bool amdgpu_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 @@ void amdgpu_atombios_crtc_overscan_setup(struct drm_crtc *crtc,
		args.usOverscanTop = cpu_to_le16(amdgpu_crtc->v_border);
		break;
	}
	amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
	amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

void amdgpu_atombios_crtc_scaler_setup(struct drm_crtc *crtc)
@@ -106,7 +106,7 @@ void amdgpu_atombios_crtc_scaler_setup(struct drm_crtc *crtc)
		args.ucEnable = ATOM_SCALER_DISABLE;
		break;
	}
	amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
	amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

void amdgpu_atombios_crtc_lock(struct drm_crtc *crtc, int lock)
@@ -123,7 +123,7 @@ void amdgpu_atombios_crtc_lock(struct drm_crtc *crtc, int lock)
	args.ucCRTC = amdgpu_crtc->crtc_id;
	args.ucEnable = lock;

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

void amdgpu_atombios_crtc_enable(struct drm_crtc *crtc, int state)
@@ -139,7 +139,7 @@ void amdgpu_atombios_crtc_enable(struct drm_crtc *crtc, int state)
	args.ucCRTC = amdgpu_crtc->crtc_id;
	args.ucEnable = state;

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

void amdgpu_atombios_crtc_blank(struct drm_crtc *crtc, int state)
@@ -155,7 +155,7 @@ void amdgpu_atombios_crtc_blank(struct drm_crtc *crtc, int state)
	args.ucCRTC = amdgpu_crtc->crtc_id;
	args.ucBlanking = state;

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

void amdgpu_atombios_crtc_powergate(struct drm_crtc *crtc, int state)
@@ -171,7 +171,7 @@ void amdgpu_atombios_crtc_powergate(struct drm_crtc *crtc, int state)
	args.ucDispPipeId = amdgpu_crtc->crtc_id;
	args.ucEnable = state;

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

void amdgpu_atombios_crtc_powergate_init(struct amdgpu_device *adev)
@@ -183,7 +183,7 @@ void amdgpu_atombios_crtc_powergate_init(struct amdgpu_device *adev)

	args.ucEnable = ATOM_INIT;

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

void amdgpu_atombios_crtc_set_dtd_timing(struct drm_crtc *crtc,
@@ -228,7 +228,7 @@ void amdgpu_atombios_crtc_set_dtd_timing(struct drm_crtc *crtc,
	args.susModeMiscInfo.usAccess = cpu_to_le16(misc);
	args.ucCRTC = amdgpu_crtc->crtc_id;

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

union atom_enable_ss {
@@ -293,7 +293,7 @@ static void amdgpu_atombios_crtc_program_ss(struct amdgpu_device *adev,
	args.v3.usSpreadSpectrumStep = cpu_to_le16(ss->step);
	args.v3.ucEnable = enable;

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

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

			amdgpu_atom_execute_table(adev->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:
@@ -428,7 +428,7 @@ static u32 amdgpu_atombios_crtc_adjust_pll(struct drm_crtc *crtc,
				args.v3.sInput.ucExtTransmitterID = 0;

			amdgpu_atom_execute_table(adev->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) {
				amdgpu_crtc->pll_flags |= AMDGPU_PLL_USE_FRAC_FB_DIV;
@@ -514,7 +514,7 @@ void amdgpu_atombios_crtc_set_disp_eng_pll(struct amdgpu_device *adev,
		DRM_ERROR("Unknown table version %d %d\n", frev, crev);
		return;
	}
	amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
	amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
}

union set_dce_clock {
@@ -544,7 +544,7 @@ u32 amdgpu_atombios_crtc_set_dce_clock(struct amdgpu_device *adev,
			args.v2_1.asParam.ulDCEClkFreq = cpu_to_le32(freq); /* 10kHz units */
			args.v2_1.asParam.ucDCEClkType = clk_type;
			args.v2_1.asParam.ucDCEClkSrc = clk_src;
			amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
			amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args, sizeof(args));
			ret_freq = le32_to_cpu(args.v2_1.asParam.ulDCEClkFreq) * 10;
			break;
		default:
@@ -740,7 +740,7 @@ void amdgpu_atombios_crtc_program_pll(struct drm_crtc *crtc,
		return;
	}

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

int amdgpu_atombios_crtc_prepare_pll(struct drm_crtc *crtc,
Loading