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 for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1428611100-23337-1-git-send-email-mjg59@srcf.ucam.org>
Date:	Thu,  9 Apr 2015 13:25:00 -0700
From:	Matthew Garrett <mjg59@...f.ucam.org>
To:	x86@...nel.org
Cc:	hpa@...or.com, mingo@...hat.com, tglx@...utronix.de,
	linux-kernel@...r.kernel.org, Matthew Garrett <mjg59@...eos.com>
Subject: [PATCH V2] x86: Allow built-in command line to work in early kernel init

From: Matthew Garrett <mjg59@...eos.com>

The kernel supports having a command line built into it. Unfortunately this
doesn't work in all cases - the built-in command line is only appended
after we've jumped to the kernel proper, but various parts of the early
boot process also pay attention to the command line.

This patch moves the command line override code from the kernel itself to
the early init code. Unfortunately the kernel can be executed by jumping
to the 16-bit entry point, the UEFI entry point, directly to the 32-bit
entry point or even to the entry point of the uncompressed image, and
there is no guarantee that any of these will have access to data held in
the others. As a result, four copies of the command line will be embedded
in the kernel.

This patch also defines a new field in boot_params in order to allow the
earlier entry points to inform the generic setup code that the command line
has already been appended and so shouldn't be added once more.

Signed-off-by: Matthew Garrett <mjg59@...eos.com>
---

V2: Use some empty data in bootparams rather than in the setup header since
there's no need for the bootloader to touch this. Add back the code to
kernel/setup.c because some scenarios may involve skipping the setup code
entirely.

 Documentation/x86/zero-page.txt                |  1 +
 arch/x86/boot/boot.h                           | 10 ++++++
 arch/x86/boot/cmdline.c                        | 37 +++++++++++++++++++
 arch/x86/boot/compressed/cmdline.c             | 18 +++++++++-
 arch/x86/boot/compressed/eboot.c               |  4 ++-
 arch/x86/boot/compressed/misc.c                |  2 ++
 arch/x86/boot/compressed/misc.h                |  3 +-
 arch/x86/boot/main.c                           |  3 ++
 arch/x86/include/uapi/asm/bootparam.h          |  5 ++-
 arch/x86/kernel/setup.c                        | 16 +++++----
 drivers/firmware/efi/libstub/efi-stub-helper.c | 49 ++++++++++++++++++++++++--
 11 files changed, 135 insertions(+), 13 deletions(-)

diff --git a/Documentation/x86/zero-page.txt b/Documentation/x86/zero-page.txt
index 82fbdbc..22eaecf 100644
--- a/Documentation/x86/zero-page.txt
+++ b/Documentation/x86/zero-page.txt
@@ -12,6 +12,7 @@ Offset	Proto	Name		Meaning
 000/040	ALL	screen_info	Text mode or frame buffer information
 				(struct screen_info)
 040/014	ALL	apm_bios_info	APM BIOS information (struct apm_bios_info)
+054/004	ALL	setup_flags	Flags passed from early kernel setup
 058/008	ALL	tboot_addr      Physical address of tboot shared page
 060/010	ALL	ist_info	Intel SpeedStep (IST) BIOS support information
 				(struct ist_info)
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
index bd49ec6..3718244 100644
--- a/arch/x86/boot/boot.h
+++ b/arch/x86/boot/boot.h
@@ -273,6 +273,7 @@ void intcall(u8 int_no, const struct biosregs *ireg, struct biosregs *oreg);
 /* cmdline.c */
 int __cmdline_find_option(unsigned long cmdline_ptr, const char *option, char *buffer, int bufsize);
 int __cmdline_find_option_bool(unsigned long cmdline_ptr, const char *option);
+int __cmdline_init(unsigned long cmdline_ptr, struct boot_params *params);
 static inline int cmdline_find_option(const char *option, char *buffer, int bufsize)
 {
 	unsigned long cmd_line_ptr = boot_params.hdr.cmd_line_ptr;
@@ -293,6 +294,15 @@ static inline int cmdline_find_option_bool(const char *option)
 	return __cmdline_find_option_bool(cmd_line_ptr, option);
 }
 
+static inline int cmdline_init(void)
+{
+	unsigned long cmd_line_ptr = boot_params.hdr.cmd_line_ptr;
+
+	if (cmd_line_ptr >= 0x100000)
+		return -1;      /* inaccessible */
+
+	return __cmdline_init(cmd_line_ptr, &boot_params);
+}
 /* cpu.c, cpucheck.c */
 int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr);
 int validate_cpu(void);
diff --git a/arch/x86/boot/cmdline.c b/arch/x86/boot/cmdline.c
index 625d21b..2273d27 100644
--- a/arch/x86/boot/cmdline.c
+++ b/arch/x86/boot/cmdline.c
@@ -14,6 +14,10 @@
 
 #include "boot.h"
 
+#ifdef CONFIG_CMDLINE_BOOL
+static char builtin_cmdline[] = CONFIG_CMDLINE;
+#endif
+
 static inline int myisspace(u8 c)
 {
 	return c <= ' ';	/* Close enough approximation */
@@ -156,3 +160,36 @@ int __cmdline_find_option_bool(unsigned long cmdline_ptr, const char *option)
 
 	return 0;	/* Buffer overrun */
 }
+
+int __cmdline_init(unsigned long cmdline_ptr, struct boot_params *params)
+{
+#ifdef CONFIG_CMDLINE_BOOL
+	addr_t cptr;
+	int i = 0;
+
+	if (!cmdline_ptr)
+		return -1;      /* No command line */
+
+	set_fs(cmdline_ptr >> 4);
+	cptr = cmdline_ptr & 0xf;
+
+#ifndef CONFIG_CMDLINE_OVERRIDE
+	while (cptr < 0x10000) {
+		char c = rdfs8(cptr);
+		if (!c) {
+			wrfs8(' ', cptr++);
+			break;
+		}
+		cptr++;
+	}
+#endif /* !CONFIG_CMDLINE_OVERRIDE */
+	while (builtin_cmdline[i] && cptr < 0xffff)
+		wrfs8(builtin_cmdline[i++], cptr++);
+
+	wrfs8('\0', cptr);
+
+	params->setup_flags |= SETUP_CMDLINE_APPENDED;
+#endif /* CONFIG_CMDLINE_BOOL */
+
+	return 0;
+}
diff --git a/arch/x86/boot/compressed/cmdline.c b/arch/x86/boot/compressed/cmdline.c
index b68e303..bf8ea07 100644
--- a/arch/x86/boot/compressed/cmdline.c
+++ b/arch/x86/boot/compressed/cmdline.c
@@ -1,6 +1,6 @@
 #include "misc.h"
 
-#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE
+#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE || CONFIG_CMDLINE_BOOL
 
 static unsigned long fs;
 static inline void set_fs(unsigned long seg)
@@ -12,6 +12,10 @@ static inline char rdfs8(addr_t addr)
 {
 	return *((char *)(fs + addr));
 }
+static inline void wrfs8(u8 v, addr_t addr)
+{
+	*((char *)(fs + addr)) = v;
+}
 #include "../cmdline.c"
 static unsigned long get_cmd_line_ptr(void)
 {
@@ -30,4 +34,16 @@ int cmdline_find_option_bool(const char *option)
 	return __cmdline_find_option_bool(get_cmd_line_ptr(), option);
 }
 
+int cmdline_init(void)
+{
+	if (!(real_mode->setup_flags & SETUP_CMDLINE_APPENDED))
+		return __cmdline_init(get_cmd_line_ptr(), real_mode);
+	return 0;
+}
+#else
+int cmdline_init(void)
+{
+#error "BAD"
+	return 0;
+}
 #endif
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index ef17683..15a932c 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -1109,7 +1109,9 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	if (!cmdline_ptr)
 		goto fail;
 	hdr->cmd_line_ptr = (unsigned long)cmdline_ptr;
-
+#ifdef CONFIG_CMDLINE_BOOL
+	boot_params->setup_flags |= SETUP_CMDLINE_APPENDED;
+#endif
 	hdr->ramdisk_image = 0;
 	hdr->ramdisk_size = 0;
 
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index a950864..dc8a287 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -390,6 +390,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
 	lines = real_mode->screen_info.orig_video_lines;
 	cols = real_mode->screen_info.orig_video_cols;
 
+	cmdline_init();
+
 	console_init();
 	debug_putstr("early console in decompress_kernel\n");
 
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 04477d6..9cbec86 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -48,10 +48,11 @@ static inline void debug_putstr(const char *s)
 
 #endif
 
-#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE
+#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE || CONFIG_CMDLINE_BOOL
 /* cmdline.c */
 int cmdline_find_option(const char *option, char *buffer, int bufsize);
 int cmdline_find_option_bool(const char *option);
+int cmdline_init(void);
 #endif
 
 
diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c
index fd6c9f2..7c24862 100644
--- a/arch/x86/boot/main.c
+++ b/arch/x86/boot/main.c
@@ -137,6 +137,9 @@ void main(void)
 	/* First, copy the boot header into the "zeropage" */
 	copy_boot_params();
 
+	/* Handle built-in command line */
+	cmdline_init();
+
 	/* Initialize the early-boot console */
 	console_init();
 	if (cmdline_find_option_bool("debug"))
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
index 89a033a..2dfb945 100644
--- a/arch/x86/include/uapi/asm/bootparam.h
+++ b/arch/x86/include/uapi/asm/bootparam.h
@@ -27,6 +27,9 @@
 #define XLF_EFI_HANDOVER_64		(1<<3)
 #define XLF_EFI_KEXEC			(1<<4)
 
+/* setup_flags */
+#define SETUP_CMDLINE_APPENDED		(1<<0)
+
 #ifndef __ASSEMBLY__
 
 #include <linux/types.h>
@@ -114,7 +117,7 @@ struct efi_info {
 struct boot_params {
 	struct screen_info screen_info;			/* 0x000 */
 	struct apm_bios_info apm_bios_info;		/* 0x040 */
-	__u8  _pad2[4];					/* 0x054 */
+	__u32 setup_flags;				/* 0x054 */
 	__u64  tboot_addr;				/* 0x058 */
 	struct ist_info ist_info;			/* 0x060 */
 	__u8  _pad3[16];				/* 0x070 */
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 0a2421c..b0d50b1 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -969,16 +969,18 @@ void __init setup_arch(char **cmdline_p)
 	bss_resource.end = __pa_symbol(__bss_stop)-1;
 
 #ifdef CONFIG_CMDLINE_BOOL
+	if (!(boot_params.setup_flags & SETUP_CMDLINE_APPENDED)) {
 #ifdef CONFIG_CMDLINE_OVERRIDE
-	strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
-#else
-	if (builtin_cmdline[0]) {
-		/* append boot loader cmdline to builtin */
-		strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE);
-		strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE);
 		strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
-	}
+#else
+		if (builtin_cmdline[0]) {
+			/* append boot loader cmdline to builtin */
+			strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE);
+			strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE);
+			strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
+		}
 #endif
+	}
 #endif
 
 	strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index f07d4a6..54afe1f 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -12,9 +12,14 @@
 
 #include <linux/efi.h>
 #include <asm/efi.h>
+#include <asm/setup.h>
 
 #include "efistub.h"
 
+#ifdef CONFIG_CMDLINE_BOOL
+static char builtin_cmdline[] = CONFIG_CMDLINE;
+#endif
+
 /*
  * Some firmware implementations have problems reading files in one go.
  * A read chunk size of 1MB seems to work for most platforms.
@@ -649,6 +654,21 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)
 	return dst;
 }
 
+static size_t efi_strlcat(char *dest, const char *src, size_t count)
+{
+	size_t dsize = strlen(dest);
+	size_t len = strlen(src);
+	size_t res = dsize + len;
+
+	dest += dsize;
+	count -= dsize;
+	if (len >= count)
+		len = count-1;
+	memcpy(dest, src, len);
+	dest[len] = 0;
+	return res;
+}
+
 /*
  * Convert the unicode UEFI command line to ASCII to pass to kernel.
  * Size of memory allocated return in *cmd_line_len.
@@ -658,14 +678,16 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
 			  efi_loaded_image_t *image,
 			  int *cmd_line_len)
 {
+	unsigned long cmdline_addr = 0;
+	int i;
+	efi_status_t status;
+#ifndef CONFIG_CMDLINE_OVERRIDE
 	const u16 *s2;
 	u8 *s1 = NULL;
-	unsigned long cmdline_addr = 0;
 	int load_options_chars = image->load_options_size / 2; /* UTF-16 */
 	const u16 *options = image->load_options;
 	int options_bytes = 0;  /* UTF-8 bytes */
 	int options_chars = 0;  /* UTF-16 chars */
-	efi_status_t status;
 	u16 zero = 0;
 
 	if (options) {
@@ -684,6 +706,12 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
 
 	options_bytes++;	/* NUL termination */
 
+#ifdef CONFIG_CMDLINE_BOOL
+	/* Add length of the built-in command line, plus a space */
+	options_bytes += strlen(builtin_cmdline);
+	options_bytes++;
+#endif
+
 	status = efi_low_alloc(sys_table_arg, options_bytes, 0, &cmdline_addr);
 	if (status != EFI_SUCCESS)
 		return NULL;
@@ -694,6 +722,23 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg,
 	s1 = efi_utf16_to_utf8(s1, s2, options_chars);
 	*s1 = '\0';
 
+#ifdef CONFIG_CMDLINE_BOOL
+	efi_strlcat((char *)cmdline_addr, " ", COMMAND_LINE_SIZE);
+	efi_strlcat((char *)cmdline_addr, builtin_cmdline, COMMAND_LINE_SIZE);
+#endif
 	*cmd_line_len = options_bytes;
+#else /* CONFIG_CMDLINE_OVERRIDE */
+	status = efi_low_alloc(sys_table_arg, strlen(builtin_cmdline), 0,
+			       &cmdline_addr);
+	if (status != EFI_SUCCESS)
+		return NULL;
+	while (builtin_cmdline[i] && i < COMMAND_LINE_SIZE) {
+		((char *)cmdline_addr)[i] = builtin_cmdline[i];
+		i++;
+	}
+	((char *)cmdline_addr)[i] = '\0';
+	*cmd_line_len = strlen(builtin_cmdline);
+#endif /* CONFIG_CMDLINE_OVERRIDE */
+
 	return (char *)cmdline_addr;
 }
-- 
2.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ