Commit 7fdaa640 authored by Josh Poimboeuf's avatar Josh Poimboeuf
Browse files

objtool: Handle Clang RSP musical chairs



For no apparent reason (possibly related to CONFIG_KMSAN), Clang can
randomly pass the value of RSP to other registers and then back again to
RSP.  Handle that accordingly.

Fixes the following warnings:

  drivers/input/misc/uinput.o: warning: objtool: uinput_str_to_user+0x165: undefined stack state
  drivers/input/misc/uinput.o: warning: objtool: uinput_str_to_user+0x165: unknown CFA base reg -1

Reported-by: default avatarArnd Bergmann <arnd@arndb.de>
Closes: https://lore.kernel.org/90956545-2066-46e3-b547-10c884582eb0@app.fastmail.com
Link: https://patch.msgid.link/240e6a172cc73292499334a3724d02ccb3247fc7.1772818491.git.jpoimboe@kernel.org


Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
parent 1fd1dc41
Loading
Loading
Loading
Loading
+23 −39
Original line number Diff line number Diff line
@@ -395,19 +395,17 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
		if (!rex_w)
			break;

		if (modrm_reg == CFI_SP) {

		if (mod_is_reg()) {
				/* mov %rsp, reg */
			/* mov reg, reg */
			ADD_OP(op) {
				op->src.type = OP_SRC_REG;
					op->src.reg = CFI_SP;
				op->src.reg = modrm_reg;
				op->dest.type = OP_DEST_REG;
				op->dest.reg = modrm_rm;
			}
			break;
		}

			} else {
		/* skip RIP relative displacement */
		if (is_RIP())
			break;
@@ -420,6 +418,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
		}

		/* mov %rsp, disp(%reg) */
		if (modrm_reg == CFI_SP) {
			ADD_OP(op) {
				op->src.type = OP_SRC_REG;
				op->src.reg = CFI_SP;
@@ -430,21 +429,6 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
			break;
		}

			break;
		}

		if (rm_is_reg(CFI_SP)) {

			/* mov reg, %rsp */
			ADD_OP(op) {
				op->src.type = OP_SRC_REG;
				op->src.reg = modrm_reg;
				op->dest.type = OP_DEST_REG;
				op->dest.reg = CFI_SP;
			}
			break;
		}

		fallthrough;
	case 0x88:
		if (!rex_w)
+14 −0
Original line number Diff line number Diff line
@@ -3000,6 +3000,20 @@ static int update_cfi_state(struct instruction *insn,
				cfi->stack_size += 8;
			}

			else if (cfi->vals[op->src.reg].base == CFI_CFA) {
				/*
				 * Clang RSP musical chairs:
				 *
				 *   mov %rsp, %rdx [handled above]
				 *   ...
				 *   mov %rdx, %rbx [handled here]
				 *   ...
				 *   mov %rbx, %rsp [handled above]
				 */
				cfi->vals[op->dest.reg].base = CFI_CFA;
				cfi->vals[op->dest.reg].offset = cfi->vals[op->src.reg].offset;
			}


			break;