lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251113164917.2563486-23-alexandre.chartre@oracle.com>
Date: Thu, 13 Nov 2025 17:49:11 +0100
From: Alexandre Chartre <alexandre.chartre@...cle.com>
To: linux-kernel@...r.kernel.org, mingo@...nel.org, jpoimboe@...nel.org,
        peterz@...radead.org
Cc: alexandre.chartre@...cle.com
Subject: [PATCH v4 22/28] objtool: Fix address references in alternatives

When using the --disas option, alternatives are disassembled but
address references in non-default alternatives can be incorrect.

The problem is that alternatives are shown as if they were replacing the
original code of the alternative. So if an alternative is referencing
an address inside the alternative then the reference has to be
adjusted to the location of the original code.

Signed-off-by: Alexandre Chartre <alexandre.chartre@...cle.com>
---
 tools/objtool/disas.c | 70 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 66 insertions(+), 4 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 6ec241806f5c4..e506fdda4d897 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -24,6 +24,7 @@
 struct disas_context {
 	struct objtool_file *file;
 	struct instruction *insn;
+	bool alt_applied;
 	char result[DISAS_RESULT_SIZE];
 	disassembler_ftype disassembler;
 	struct disassemble_info info;
@@ -160,6 +161,43 @@ static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
 	}
 }
 
+static bool disas_print_addr_alt(bfd_vma addr, struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *orig_first_insn;
+	struct alt_group *alt_group;
+	unsigned long offset;
+	struct symbol *sym;
+
+	/*
+	 * Check if we are processing an alternative at the original
+	 * instruction address (i.e. if alt_applied is true) and if
+	 * we are referencing an address inside the alternative.
+	 *
+	 * For example, this happens if there is a branch inside an
+	 * alternative. In that case, the address should be updated
+	 * to a reference inside the original instruction flow.
+	 */
+	if (!dctx->alt_applied)
+		return false;
+
+	alt_group = dctx->insn->alt_group;
+	if (!alt_group || !alt_group->orig_group ||
+	    addr < alt_group->first_insn->offset ||
+	    addr > alt_group->last_insn->offset)
+		return false;
+
+	orig_first_insn = alt_group->orig_group->first_insn;
+	offset = addr - alt_group->first_insn->offset;
+
+	addr = orig_first_insn->offset + offset;
+	sym = orig_first_insn->sym;
+
+	disas_print_addr_sym(orig_first_insn->sec, sym, addr, dinfo);
+
+	return true;
+}
+
 static void disas_print_addr_noreloc(bfd_vma addr,
 				     struct disassemble_info *dinfo)
 {
@@ -167,6 +205,9 @@ static void disas_print_addr_noreloc(bfd_vma addr,
 	struct instruction *insn = dctx->insn;
 	struct symbol *sym = NULL;
 
+	if (disas_print_addr_alt(addr, dinfo))
+		return;
+
 	if (insn->sym && addr >= insn->sym->offset &&
 	    addr < insn->sym->offset + insn->sym->len) {
 		sym = insn->sym;
@@ -235,8 +276,9 @@ static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
 	 */
 	jump_dest = insn->jump_dest;
 	if (jump_dest && jump_dest->sym && jump_dest->offset == addr) {
-		disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
-				     addr, dinfo);
+		if (!disas_print_addr_alt(addr, dinfo))
+			disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
+					     addr, dinfo);
 		return;
 	}
 
@@ -486,13 +528,22 @@ void disas_print_insn(FILE *stream, struct disas_context *dctx,
 
 /*
  * Disassemble a single instruction. Return the size of the instruction.
+ *
+ * If alt_applied is true then insn should be an instruction from of an
+ * alternative (i.e. insn->alt_group != NULL), and it is disassembled
+ * at the location of the original code it is replacing. When the
+ * instruction references any address inside the alternative then
+ * these references will be re-adjusted to replace the original code.
  */
-size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
+static size_t disas_insn_common(struct disas_context *dctx,
+				struct instruction *insn,
+				bool alt_applied)
 {
 	disassembler_ftype disasm = dctx->disassembler;
 	struct disassemble_info *dinfo = &dctx->info;
 
 	dctx->insn = insn;
+	dctx->alt_applied = alt_applied;
 	dctx->result[0] = '\0';
 
 	if (insn->type == INSN_NOP) {
@@ -511,6 +562,17 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 	return disasm(insn->offset, &dctx->info);
 }
 
+size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
+{
+	return disas_insn_common(dctx, insn, false);
+}
+
+static size_t disas_insn_alt(struct disas_context *dctx,
+			     struct instruction *insn)
+{
+	return disas_insn_common(dctx, insn, true);
+}
+
 static struct instruction *next_insn_same_alt(struct objtool_file *file,
 					      struct alt_group *alt_grp,
 					      struct instruction *insn)
@@ -682,7 +744,7 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
-		disas_insn(dctx, insn);
+		disas_insn_alt(dctx, insn);
 		str = strdup(disas_result(dctx));
 		if (!str)
 			return -1;
-- 
2.43.5


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ