Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/speculation: Allow IBPB to be conditionally enabled on CPUs with always-on STIBP

On AMD CPUs which have the feature X86_FEATURE_AMD_STIBP_ALWAYS_ON,
STIBP is set to on and

spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED

At the same time, IBPB can be set to conditional.

However, this leads to the case where it's impossible to turn on IBPB
for a process because in the PR_SPEC_DISABLE case in ib_prctl_set() the

spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED

condition leads to a return before the task flag is set. Similarly,
ib_prctl_get() will return PR_SPEC_DISABLE even though IBPB is set to
conditional.

More generally, the following cases are possible:

1. STIBP = conditional && IBPB = on for spectre_v2_user=seccomp,ibpb
2. STIBP = on && IBPB = conditional for AMD CPUs with
X86_FEATURE_AMD_STIBP_ALWAYS_ON

The first case functions correctly today, but only because
spectre_v2_user_ibpb isn't updated to reflect the IBPB mode.

At a high level, this change does one thing. If either STIBP or IBPB
is set to conditional, allow the prctl to change the task flag.
Also, reflect that capability when querying the state. This isn't
perfect since it doesn't take into account if only STIBP or IBPB is
unconditionally on. But it allows the conditional feature to work as
expected, without affecting the unconditional one.

[ bp: Massage commit message and comment; space out statements for
better readability. ]

Fixes: 21998a351512 ("x86/speculation: Avoid force-disabling IBPB based on STIBP and enhanced IBRS.")
Signed-off-by: Anand K Mistry <amistry@google.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Tom Lendacky <thomas.lendacky@amd.com>
Link: https://lkml.kernel.org/r/20201105163246.v2.1.Ifd7243cd3e2c2206a893ad0a5b9a4f19549e22c6@changeid

authored by

Anand K Mistry and committed by
Borislav Petkov
1978b3a5 4d6ffa27

+33 -18
+33 -18
arch/x86/kernel/cpu/bugs.c
··· 1254 1254 return 0; 1255 1255 } 1256 1256 1257 + static bool is_spec_ib_user_controlled(void) 1258 + { 1259 + return spectre_v2_user_ibpb == SPECTRE_V2_USER_PRCTL || 1260 + spectre_v2_user_ibpb == SPECTRE_V2_USER_SECCOMP || 1261 + spectre_v2_user_stibp == SPECTRE_V2_USER_PRCTL || 1262 + spectre_v2_user_stibp == SPECTRE_V2_USER_SECCOMP; 1263 + } 1264 + 1257 1265 static int ib_prctl_set(struct task_struct *task, unsigned long ctrl) 1258 1266 { 1259 1267 switch (ctrl) { ··· 1269 1261 if (spectre_v2_user_ibpb == SPECTRE_V2_USER_NONE && 1270 1262 spectre_v2_user_stibp == SPECTRE_V2_USER_NONE) 1271 1263 return 0; 1264 + 1272 1265 /* 1273 - * Indirect branch speculation is always disabled in strict 1274 - * mode. It can neither be enabled if it was force-disabled 1275 - * by a previous prctl call. 1266 + * With strict mode for both IBPB and STIBP, the instruction 1267 + * code paths avoid checking this task flag and instead, 1268 + * unconditionally run the instruction. However, STIBP and IBPB 1269 + * are independent and either can be set to conditionally 1270 + * enabled regardless of the mode of the other. 1271 + * 1272 + * If either is set to conditional, allow the task flag to be 1273 + * updated, unless it was force-disabled by a previous prctl 1274 + * call. Currently, this is possible on an AMD CPU which has the 1275 + * feature X86_FEATURE_AMD_STIBP_ALWAYS_ON. In this case, if the 1276 + * kernel is booted with 'spectre_v2_user=seccomp', then 1277 + * spectre_v2_user_ibpb == SPECTRE_V2_USER_SECCOMP and 1278 + * spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED. 1276 1279 */ 1277 - if (spectre_v2_user_ibpb == SPECTRE_V2_USER_STRICT || 1278 - spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT || 1279 - spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED || 1280 + if (!is_spec_ib_user_controlled() || 1280 1281 task_spec_ib_force_disable(task)) 1281 1282 return -EPERM; 1283 + 1282 1284 task_clear_spec_ib_disable(task); 1283 1285 task_update_spec_tif(task); 1284 1286 break; ··· 1301 1283 if (spectre_v2_user_ibpb == SPECTRE_V2_USER_NONE && 1302 1284 spectre_v2_user_stibp == SPECTRE_V2_USER_NONE) 1303 1285 return -EPERM; 1304 - if (spectre_v2_user_ibpb == SPECTRE_V2_USER_STRICT || 1305 - spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT || 1306 - spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED) 1286 + 1287 + if (!is_spec_ib_user_controlled()) 1307 1288 return 0; 1289 + 1308 1290 task_set_spec_ib_disable(task); 1309 1291 if (ctrl == PR_SPEC_FORCE_DISABLE) 1310 1292 task_set_spec_ib_force_disable(task); ··· 1369 1351 if (spectre_v2_user_ibpb == SPECTRE_V2_USER_NONE && 1370 1352 spectre_v2_user_stibp == SPECTRE_V2_USER_NONE) 1371 1353 return PR_SPEC_ENABLE; 1372 - else if (spectre_v2_user_ibpb == SPECTRE_V2_USER_STRICT || 1373 - spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT || 1374 - spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED) 1375 - return PR_SPEC_DISABLE; 1376 - else if (spectre_v2_user_ibpb == SPECTRE_V2_USER_PRCTL || 1377 - spectre_v2_user_ibpb == SPECTRE_V2_USER_SECCOMP || 1378 - spectre_v2_user_stibp == SPECTRE_V2_USER_PRCTL || 1379 - spectre_v2_user_stibp == SPECTRE_V2_USER_SECCOMP) { 1354 + else if (is_spec_ib_user_controlled()) { 1380 1355 if (task_spec_ib_force_disable(task)) 1381 1356 return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; 1382 1357 if (task_spec_ib_disable(task)) 1383 1358 return PR_SPEC_PRCTL | PR_SPEC_DISABLE; 1384 1359 return PR_SPEC_PRCTL | PR_SPEC_ENABLE; 1385 - } else 1360 + } else if (spectre_v2_user_ibpb == SPECTRE_V2_USER_STRICT || 1361 + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT || 1362 + spectre_v2_user_stibp == SPECTRE_V2_USER_STRICT_PREFERRED) 1363 + return PR_SPEC_DISABLE; 1364 + else 1386 1365 return PR_SPEC_NOT_AFFECTED; 1387 1366 } 1388 1367