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]
Date: Fri, 22 Mar 2024 12:52:24 -0400
From: Brian Gerst <brgerst@...il.com>
To: linux-kernel@...r.kernel.org,
	x86@...nel.org
Cc: Ingo Molnar <mingo@...nel.org>,
	Thomas Gleixner <tglx@...utronix.de>,
	Borislav Petkov <bp@...en8.de>,
	"H . Peter Anvin" <hpa@...or.com>,
	Uros Bizjak <ubizjak@...il.com>,
	David.Laight@...lab.com,
	Brian Gerst <brgerst@...il.com>
Subject: [PATCH v4 07/16] objtool: Convert fixed location stack protector accesses

Older versions of GCC fixed the location of the stack protector canary
at %gs:40.  Use objtool to convert these accesses to normal percpu
accesses to __stack_chk_guard.

Signed-off-by: Brian Gerst <brgerst@...il.com>
---
 arch/x86/Kconfig                        |  4 ++
 scripts/Makefile.lib                    |  2 +
 tools/objtool/arch/x86/decode.c         | 46 +++++++++++++
 tools/objtool/arch/x86/special.c        | 91 +++++++++++++++++++++++++
 tools/objtool/builtin-check.c           |  9 ++-
 tools/objtool/check.c                   | 12 ++++
 tools/objtool/elf.c                     | 34 +++++++--
 tools/objtool/include/objtool/arch.h    |  3 +
 tools/objtool/include/objtool/builtin.h |  2 +
 tools/objtool/include/objtool/elf.h     |  6 ++
 10 files changed, 204 insertions(+), 5 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 88d72227e3cb..121cfb9ffc0e 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -417,6 +417,10 @@ config CC_HAS_SANE_STACKPROTECTOR
 	  We have to make sure stack protector is unconditionally disabled if
 	  the compiler does not allow control of the segment and symbol.
 
+config STACKPROTECTOR_OBJTOOL
+	bool
+	default n
+
 menu "Processor type and features"
 
 config SMP
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 1bd59b8db05f..6bc4c69a9e50 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -258,6 +258,8 @@ objtool := $(objtree)/tools/objtool/objtool
 objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK)		+= --hacks=jump_label
 objtool-args-$(CONFIG_HAVE_NOINSTR_HACK)		+= --hacks=noinstr
 objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING)	+= --hacks=skylake
+objtool-args-$(CONFIG_STACKPROTECTOR_OBJTOOL)		+= --hacks=stackprotector
+objtool-args-$(CONFIG_SMP)				+= --smp
 objtool-args-$(CONFIG_X86_KERNEL_IBT)			+= --ibt
 objtool-args-$(CONFIG_FINEIBT)				+= --cfi
 objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL)	+= --mcount
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 3a1d80a7878d..583a16b8bf47 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -144,6 +144,18 @@ static bool has_notrack_prefix(struct insn *insn)
 	return false;
 }
 
+static bool has_gs_prefix(struct insn *insn)
+{
+	int i;
+
+	for (i = 0; i < insn->prefixes.nbytes; i++) {
+		if (insn->prefixes.bytes[i] == 0x65)
+			return true;
+	}
+
+	return false;
+}
+
 int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
 			    unsigned long offset, unsigned int maxlen,
 			    struct instruction *insn)
@@ -408,10 +420,44 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
 
 		break;
 
+	case 0x2b:
+	case 0x3b:
+	case 0x39:
+		if (!rex_w)
+			break;
+
+		/* sub %gs:0x28, reg */
+		/* cmp %gs:0x28, reg */
+		/* cmp reg, %gs:0x28 */
+		if (has_gs_prefix(&ins) &&
+		    modrm_mod == 0 &&
+		    modrm_rm == 4 &&
+		    sib_index == 4 &&
+		    sib_base == 5 &&
+		    ins.displacement.value == 0x28)
+		{
+			insn->type = INSN_STACKPROTECTOR;
+			break;
+		}
+
+		break;
+
 	case 0x8b:
 		if (!rex_w)
 			break;
 
+		/* mov %gs:0x28, reg */
+		if (has_gs_prefix(&ins) &&
+		    modrm_mod == 0 &&
+		    modrm_rm == 4 &&
+		    sib_index == 4 &&
+		    sib_base == 5 &&
+		    ins.displacement.value == 0x28)
+		{
+			insn->type = INSN_STACKPROTECTOR;
+			break;
+		}
+
 		if (rm_is_mem(CFI_BP)) {
 
 			/* mov disp(%rbp), reg */
diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c
index 4134d27c696b..020b6040c487 100644
--- a/tools/objtool/arch/x86/special.c
+++ b/tools/objtool/arch/x86/special.c
@@ -3,6 +3,9 @@
 
 #include <objtool/special.h>
 #include <objtool/builtin.h>
+#include <objtool/warn.h>
+#include <objtool/check.h>
+#include <objtool/elf.h>
 
 #define X86_FEATURE_POPCNT (4 * 32 + 23)
 #define X86_FEATURE_SMAP   (9 * 32 + 20)
@@ -137,3 +140,91 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 
 	return rodata_reloc;
 }
+
+/*
+ * Convert op %gs:0x28, reg -> op __stack_chk_guard(%rip), reg
+ * op is MOV, SUB, or CMP.
+ *
+ * This can be removed when the minimum supported GCC version is raised
+ * to 8.1 or later.
+ */
+int arch_hack_stackprotector(struct objtool_file *file)
+{
+	struct section *sec;
+	struct symbol *__stack_chk_guard;
+	struct instruction *insn;
+
+	int i;
+
+	__stack_chk_guard = find_symbol_by_name(file->elf, "__stack_chk_guard");
+
+	for_each_sec(file, sec) {
+		int count = 0;
+		int idx;
+		struct section *rsec = sec->rsec;
+
+		sec_for_each_insn(file, sec, insn) {
+			if (insn->type == INSN_STACKPROTECTOR)
+				count++;
+		}
+
+		if (!count)
+			continue;
+
+		if (!__stack_chk_guard)
+			__stack_chk_guard = elf_create_undef_symbol(file->elf, "__stack_chk_guard");
+
+		if (!rsec) {
+			idx = 0;
+			rsec = sec->rsec = elf_create_rela_section(file->elf, sec, count);
+		} else {
+			idx = sec_num_entries(rsec);
+			if (elf_extend_rela_section(file->elf, rsec, count))
+				return -1;
+		}
+
+		sec_for_each_insn(file, sec, insn) {
+			unsigned char *data = sec->data->d_buf + insn->offset;
+
+			if (insn->type != INSN_STACKPROTECTOR)
+				continue;
+
+			if (insn->len != 9)
+				goto invalid;
+
+			/* Convert GS prefix to DS if !SMP */
+			if (data[0] != 0x65)
+				goto invalid;
+			if (!opts.smp)
+				data[0] = 0x3e;
+
+			/* Set Mod=00, R/M=101.  Preserve Reg */
+			data[3] = (data[3] & 0x38) | 5;
+
+			/* Displacement 0 */
+			data[4] = 0;
+			data[5] = 0;
+			data[6] = 0;
+			data[7] = 0;
+
+			/* Pad with NOP */
+			data[8] = 0x90;
+
+			if (!elf_init_reloc_data_sym(file->elf, sec, insn->offset + 4, idx++, __stack_chk_guard, -4))
+				return -1;
+
+			continue;
+
+invalid:
+			fprintf(stderr, "Invalid stackprotector instruction at %s+0x%lx: ", sec->name, insn->offset);
+			for (i = 0; i < insn->len; i++)
+				fprintf(stderr, "%02x ", data[i]);
+			fprintf(stderr, "\n");
+			return -1;
+		}
+
+		mark_sec_changed(file->elf, sec, true);
+	}
+
+	return 0;
+}
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 5e21cfb7661d..0ab2efb45c0e 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -62,12 +62,17 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
 		found = true;
 	}
 
+	if (!str || strstr(str, "stackprotector")) {
+		opts.hack_stackprotector = true;
+		found = true;
+	}
+
 	return found ? 0 : -1;
 }
 
 static const struct option check_options[] = {
 	OPT_GROUP("Actions:"),
-	OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
+	OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake,stackprotector", "patch toolchain bugs/limitations", parse_hacks),
 	OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
 	OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
 	OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
@@ -94,6 +99,7 @@ static const struct option check_options[] = {
 	OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"),
 	OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"),
 	OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"),
+	OPT_BOOLEAN(0, "smp", &opts.smp, "building an SMP kernel"),
 
 	OPT_END(),
 };
@@ -133,6 +139,7 @@ static bool opts_valid(void)
 {
 	if (opts.hack_jump_label	||
 	    opts.hack_noinstr		||
+	    opts.hack_stackprotector	||
 	    opts.ibt			||
 	    opts.mcount			||
 	    opts.noinstr		||
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 0a2c161fc04d..0056dd99ff7f 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1315,6 +1315,11 @@ __weak bool arch_is_embedded_insn(struct symbol *sym)
 	return false;
 }
 
+__weak int arch_hack_stackprotector(struct objtool_file *file)
+{
+	return 0;
+}
+
 static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
 {
 	struct reloc *reloc;
@@ -4824,6 +4829,13 @@ int check(struct objtool_file *file)
 		warnings += ret;
 	}
 
+	if (opts.hack_stackprotector) {
+		ret = arch_hack_stackprotector(file);
+		if (ret < 0)
+			goto out;
+		warnings += ret;
+	}
+
 	free_insns(file);
 
 	if (opts.verbose)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index cfb970727c8a..2af99b2a054c 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -846,6 +846,32 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
 	return sym;
 }
 
+struct symbol *
+elf_create_undef_symbol(struct elf *elf, const char *sym_name)
+{
+	struct symbol *sym = calloc(1, sizeof(*sym));
+	char *name = strdup(sym_name);
+
+	if (!sym || !name) {
+		perror("malloc");
+		return NULL;
+	}
+
+	sym->name = name;
+	sym->sec = find_section_by_index(elf, 0);
+
+	sym->sym.st_name = elf_add_string(elf, NULL, name);
+	sym->sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE);
+	sym->sym.st_value = 0;
+	sym->sym.st_size = 0;
+
+	sym = __elf_create_symbol(elf, sym);
+	if (sym)
+		elf_add_symbol(elf, sym);
+
+	return sym;
+}
+
 static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
 				    unsigned int reloc_idx,
 				    unsigned long offset, struct symbol *sym,
@@ -924,7 +950,7 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
 				      struct symbol *sym,
 				      s64 addend)
 {
-	if (sym->sec && (sec->sh.sh_flags & SHF_EXECINSTR)) {
+	if (sym->sec && (sym->sec->sh.sh_flags & SHF_EXECINSTR)) {
 		WARN("bad call to %s() for text symbol %s",
 		     __func__, sym->name);
 		return NULL;
@@ -1196,9 +1222,9 @@ struct section *elf_create_section(struct elf *elf, const char *name,
 	return sec;
 }
 
-static struct section *elf_create_rela_section(struct elf *elf,
-					       struct section *sec,
-					       unsigned int reloc_nr)
+struct section *elf_create_rela_section(struct elf *elf,
+					struct section *sec,
+					unsigned int reloc_nr)
 {
 	struct section *rsec;
 	struct reloc_block *block;
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index 0b303eba660e..c60fec88b3af 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -28,6 +28,7 @@ enum insn_type {
 	INSN_CLD,
 	INSN_TRAP,
 	INSN_ENDBR,
+	INSN_STACKPROTECTOR,
 	INSN_OTHER,
 };
 
@@ -96,4 +97,6 @@ int arch_rewrite_retpolines(struct objtool_file *file);
 
 bool arch_pc_relative_reloc(struct reloc *reloc);
 
+int arch_hack_stackprotector(struct objtool_file *file);
+
 #endif /* _ARCH_H */
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index fcca6662c8b4..5085d3135e6b 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -13,6 +13,7 @@ struct opts {
 	bool hack_jump_label;
 	bool hack_noinstr;
 	bool hack_skylake;
+	bool hack_stackprotector;
 	bool ibt;
 	bool mcount;
 	bool noinstr;
@@ -38,6 +39,7 @@ struct opts {
 	bool sec_address;
 	bool stats;
 	bool verbose;
+	bool smp;
 };
 
 extern struct opts opts;
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index 7851467f6878..b5eec9e4a65d 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -120,6 +120,10 @@ struct elf *elf_open_read(const char *name, int flags);
 struct section *elf_create_section(struct elf *elf, const char *name,
 				   size_t entsize, unsigned int nr);
 
+struct section *elf_create_rela_section(struct elf *elf,
+					struct section *sec,
+					unsigned int reloc_nr);
+
 int elf_extend_rela_section(struct elf *elf,
 			    struct section *rsec,
 			    int add_relocs);
@@ -130,6 +134,8 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name,
 
 struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);
 
+struct symbol *elf_create_undef_symbol(struct elf *elf, const char *sym_name);
+
 struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
 				      unsigned long offset,
 				      unsigned int reloc_idx,
-- 
2.44.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ