diff mbox series

[105/118] fs/binfmt_elf.c: don't copy ELF header around

Message ID 20200131061655.5PTo3WfTa%akpm@linux-foundation.org (mailing list archive)
State New, archived
Headers show
Series [001/118] lib/test_bitmap: correct test data offsets for 32-bit | expand

Commit Message

Andrew Morton Jan. 31, 2020, 6:16 a.m. UTC
From: Alexey Dobriyan <adobriyan@gmail.com>
Subject: fs/binfmt_elf.c: don't copy ELF header around

ELF header is read into bprm->buf[] by generic execve code.

Save a memcpy and allocate just one header for the interpreter instead of
two headers (64 bytes instead of 128 on 64-bit).

Link: http://lkml.kernel.org/r/20191208171242.GA19716@avx2
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---

 fs/binfmt_elf.c |   55 ++++++++++++++++++++++------------------------
 1 file changed, 27 insertions(+), 28 deletions(-)

Comments

Jinjie Ruan Nov. 22, 2023, 7:15 a.m. UTC | #1
On 2020/1/31 14:16, Andrew Morton wrote:
> From: Alexey Dobriyan <adobriyan@gmail.com>
> Subject: fs/binfmt_elf.c: don't copy ELF header around
> 
> ELF header is read into bprm->buf[] by generic execve code.
> 
> Save a memcpy and allocate just one header for the interpreter instead of
> two headers (64 bytes instead of 128 on 64-bit).
> 
> Link: http://lkml.kernel.org/r/20191208171242.GA19716@avx2
> Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
> Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
> ---
> 
>  fs/binfmt_elf.c |   55 ++++++++++++++++++++++------------------------
>  1 file changed, 27 insertions(+), 28 deletions(-)
> 
> --- a/fs/binfmt_elf.c~elf-dont-copy-elf-header-around
> +++ a/fs/binfmt_elf.c
> @@ -161,8 +161,9 @@ static int padzero(unsigned long elf_bss
>  #endif
>  
>  static int
> -create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
> -		unsigned long load_addr, unsigned long interp_load_addr)
> +create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
> +		unsigned long load_addr, unsigned long interp_load_addr,
> +		unsigned long e_entry)
>  {
>  	unsigned long p = bprm->p;
>  	int argc = bprm->argc;
> @@ -251,7 +252,7 @@ create_elf_tables(struct linux_binprm *b
>  	NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
>  	NEW_AUX_ENT(AT_BASE, interp_load_addr);
>  	NEW_AUX_ENT(AT_FLAGS, 0);
> -	NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
> +	NEW_AUX_ENT(AT_ENTRY, e_entry);
>  	NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
>  	NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
>  	NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
> @@ -690,12 +691,13 @@ static int load_elf_binary(struct linux_
>  	int bss_prot = 0;
>  	int retval, i;
>  	unsigned long elf_entry;
> +	unsigned long e_entry;
>  	unsigned long interp_load_addr = 0;
>  	unsigned long start_code, end_code, start_data, end_data;
>  	unsigned long reloc_func_desc __maybe_unused = 0;
>  	int executable_stack = EXSTACK_DEFAULT;
> +	struct elfhdr *elf_ex = (struct elfhdr *)bprm->buf;
>  	struct {
> -		struct elfhdr elf_ex;
>  		struct elfhdr interp_elf_ex;
>  	} *loc;
>  	struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;
> @@ -706,30 +708,27 @@ static int load_elf_binary(struct linux_
>  		retval = -ENOMEM;
>  		goto out_ret;
>  	}
> -	
> -	/* Get the exec-header */
> -	loc->elf_ex = *((struct elfhdr *)bprm->buf);
>  
>  	retval = -ENOEXEC;
>  	/* First of all, some simple consistency checks */
> -	if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
> +	if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
>  		goto out;
>  
> -	if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
> +	if (elf_ex->e_type != ET_EXEC && elf_ex->e_type != ET_DYN)
>  		goto out;
> -	if (!elf_check_arch(&loc->elf_ex))
> +	if (!elf_check_arch(elf_ex))
>  		goto out;
> -	if (elf_check_fdpic(&loc->elf_ex))
> +	if (elf_check_fdpic(elf_ex))
>  		goto out;
>  	if (!bprm->file->f_op->mmap)
>  		goto out;
>  
> -	elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
> +	elf_phdata = load_elf_phdrs(elf_ex, bprm->file);
>  	if (!elf_phdata)
>  		goto out;
>  
>  	elf_ppnt = elf_phdata;
> -	for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
> +	for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
>  		char *elf_interpreter;
>  
>  		if (elf_ppnt->p_type != PT_INTERP)
> @@ -783,7 +782,7 @@ out_free_interp:
>  	}
>  
>  	elf_ppnt = elf_phdata;
> -	for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
> +	for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++)
>  		switch (elf_ppnt->p_type) {
>  		case PT_GNU_STACK:
>  			if (elf_ppnt->p_flags & PF_X)
> @@ -793,7 +792,7 @@ out_free_interp:
>  			break;
>  
>  		case PT_LOPROC ... PT_HIPROC:
> -			retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
> +			retval = arch_elf_pt_proc(elf_ex, elf_ppnt,
>  						  bprm->file, false,
>  						  &arch_state);
>  			if (retval)
> @@ -837,7 +836,7 @@ out_free_interp:
>  	 * still possible to return an error to the code that invoked
>  	 * the exec syscall.
>  	 */
> -	retval = arch_check_elf(&loc->elf_ex,
> +	retval = arch_check_elf(elf_ex,
>  				!!interpreter, &loc->interp_elf_ex,
>  				&arch_state);
>  	if (retval)
> @@ -850,8 +849,8 @@ out_free_interp:
>  
>  	/* Do this immediately, since STACK_TOP as used in setup_arg_pages
>  	   may depend on the personality.  */
> -	SET_PERSONALITY2(loc->elf_ex, &arch_state);
> -	if (elf_read_implies_exec(loc->elf_ex, executable_stack))
> +	SET_PERSONALITY2(*elf_ex, &arch_state);

It seems that the "SET_PERSONALITY2()" is a little late.

When a 32-bit compatible user-mode program forks out a 64-bit program,
when the 64-bit program is run in execve() the 32-bit STACK_TOP_MAX is
used to set vm_end and vm_start of the vma in __bprm_mm_init() in
alloc_bprm() because the 32-bit compatible flag has not been cleared,
but the setup_arg_pages() function later uses 64-bit STACK_TOP after
calling this SET_PERSONALITY2() to clear the 32-bit compatible flag,
which doesn't seem reasonable.

> +	if (elf_read_implies_exec(*elf_ex, executable_stack))
>  		current->personality |= READ_IMPLIES_EXEC;
>  
>  	if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
> @@ -878,7 +877,7 @@ out_free_interp:
>  	/* Now we do a little grungy work by mmapping the ELF image into
>  	   the correct location in memory. */
>  	for(i = 0, elf_ppnt = elf_phdata;
> -	    i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
> +	    i < elf_ex->e_phnum; i++, elf_ppnt++) {
>  		int elf_prot, elf_flags;
>  		unsigned long k, vaddr;
>  		unsigned long total_size = 0;
> @@ -922,9 +921,9 @@ out_free_interp:
>  		 * If we are loading ET_EXEC or we have already performed
>  		 * the ET_DYN load_addr calculations, proceed normally.
>  		 */
> -		if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
> +		if (elf_ex->e_type == ET_EXEC || load_addr_set) {
>  			elf_flags |= MAP_FIXED;
> -		} else if (loc->elf_ex.e_type == ET_DYN) {
> +		} else if (elf_ex->e_type == ET_DYN) {
>  			/*
>  			 * This logic is run once for the first LOAD Program
>  			 * Header for ET_DYN binaries to calculate the
> @@ -973,7 +972,7 @@ out_free_interp:
>  			load_bias = ELF_PAGESTART(load_bias - vaddr);
>  
>  			total_size = total_mapping_size(elf_phdata,
> -							loc->elf_ex.e_phnum);
> +							elf_ex->e_phnum);
>  			if (!total_size) {
>  				retval = -EINVAL;
>  				goto out_free_dentry;
> @@ -991,7 +990,7 @@ out_free_interp:
>  		if (!load_addr_set) {
>  			load_addr_set = 1;
>  			load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
> -			if (loc->elf_ex.e_type == ET_DYN) {
> +			if (elf_ex->e_type == ET_DYN) {
>  				load_bias += error -
>  				             ELF_PAGESTART(load_bias + vaddr);
>  				load_addr += load_bias;
> @@ -1032,7 +1031,7 @@ out_free_interp:
>  		}
>  	}
>  
> -	loc->elf_ex.e_entry += load_bias;
> +	e_entry = elf_ex->e_entry + load_bias;
>  	elf_bss += load_bias;
>  	elf_brk += load_bias;
>  	start_code += load_bias;
> @@ -1075,7 +1074,7 @@ out_free_interp:
>  		allow_write_access(interpreter);
>  		fput(interpreter);
>  	} else {
> -		elf_entry = loc->elf_ex.e_entry;
> +		elf_entry = e_entry;
>  		if (BAD_ADDR(elf_entry)) {
>  			retval = -EINVAL;
>  			goto out_free_dentry;
> @@ -1093,8 +1092,8 @@ out_free_interp:
>  		goto out;
>  #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
>  
> -	retval = create_elf_tables(bprm, &loc->elf_ex,
> -			  load_addr, interp_load_addr);
> +	retval = create_elf_tables(bprm, elf_ex,
> +			  load_addr, interp_load_addr, e_entry);
>  	if (retval < 0)
>  		goto out;
>  	current->mm->end_code = end_code;
> @@ -1112,7 +1111,7 @@ out_free_interp:
>  		 * growing down), and into the unused ELF_ET_DYN_BASE region.
>  		 */
>  		if (IS_ENABLED(CONFIG_ARCH_HAS_ELF_RANDOMIZE) &&
> -		    loc->elf_ex.e_type == ET_DYN && !interpreter)
> +		    elf_ex->e_type == ET_DYN && !interpreter)
>  			current->mm->brk = current->mm->start_brk =
>  				ELF_ET_DYN_BASE;
>  
> _
diff mbox series

Patch

--- a/fs/binfmt_elf.c~elf-dont-copy-elf-header-around
+++ a/fs/binfmt_elf.c
@@ -161,8 +161,9 @@  static int padzero(unsigned long elf_bss
 #endif
 
 static int
-create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
-		unsigned long load_addr, unsigned long interp_load_addr)
+create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
+		unsigned long load_addr, unsigned long interp_load_addr,
+		unsigned long e_entry)
 {
 	unsigned long p = bprm->p;
 	int argc = bprm->argc;
@@ -251,7 +252,7 @@  create_elf_tables(struct linux_binprm *b
 	NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
 	NEW_AUX_ENT(AT_BASE, interp_load_addr);
 	NEW_AUX_ENT(AT_FLAGS, 0);
-	NEW_AUX_ENT(AT_ENTRY, exec->e_entry);
+	NEW_AUX_ENT(AT_ENTRY, e_entry);
 	NEW_AUX_ENT(AT_UID, from_kuid_munged(cred->user_ns, cred->uid));
 	NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
 	NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
@@ -690,12 +691,13 @@  static int load_elf_binary(struct linux_
 	int bss_prot = 0;
 	int retval, i;
 	unsigned long elf_entry;
+	unsigned long e_entry;
 	unsigned long interp_load_addr = 0;
 	unsigned long start_code, end_code, start_data, end_data;
 	unsigned long reloc_func_desc __maybe_unused = 0;
 	int executable_stack = EXSTACK_DEFAULT;
+	struct elfhdr *elf_ex = (struct elfhdr *)bprm->buf;
 	struct {
-		struct elfhdr elf_ex;
 		struct elfhdr interp_elf_ex;
 	} *loc;
 	struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;
@@ -706,30 +708,27 @@  static int load_elf_binary(struct linux_
 		retval = -ENOMEM;
 		goto out_ret;
 	}
-	
-	/* Get the exec-header */
-	loc->elf_ex = *((struct elfhdr *)bprm->buf);
 
 	retval = -ENOEXEC;
 	/* First of all, some simple consistency checks */
-	if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
+	if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0)
 		goto out;
 
-	if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
+	if (elf_ex->e_type != ET_EXEC && elf_ex->e_type != ET_DYN)
 		goto out;
-	if (!elf_check_arch(&loc->elf_ex))
+	if (!elf_check_arch(elf_ex))
 		goto out;
-	if (elf_check_fdpic(&loc->elf_ex))
+	if (elf_check_fdpic(elf_ex))
 		goto out;
 	if (!bprm->file->f_op->mmap)
 		goto out;
 
-	elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
+	elf_phdata = load_elf_phdrs(elf_ex, bprm->file);
 	if (!elf_phdata)
 		goto out;
 
 	elf_ppnt = elf_phdata;
-	for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
+	for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
 		char *elf_interpreter;
 
 		if (elf_ppnt->p_type != PT_INTERP)
@@ -783,7 +782,7 @@  out_free_interp:
 	}
 
 	elf_ppnt = elf_phdata;
-	for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
+	for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++)
 		switch (elf_ppnt->p_type) {
 		case PT_GNU_STACK:
 			if (elf_ppnt->p_flags & PF_X)
@@ -793,7 +792,7 @@  out_free_interp:
 			break;
 
 		case PT_LOPROC ... PT_HIPROC:
-			retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
+			retval = arch_elf_pt_proc(elf_ex, elf_ppnt,
 						  bprm->file, false,
 						  &arch_state);
 			if (retval)
@@ -837,7 +836,7 @@  out_free_interp:
 	 * still possible to return an error to the code that invoked
 	 * the exec syscall.
 	 */
-	retval = arch_check_elf(&loc->elf_ex,
+	retval = arch_check_elf(elf_ex,
 				!!interpreter, &loc->interp_elf_ex,
 				&arch_state);
 	if (retval)
@@ -850,8 +849,8 @@  out_free_interp:
 
 	/* Do this immediately, since STACK_TOP as used in setup_arg_pages
 	   may depend on the personality.  */
-	SET_PERSONALITY2(loc->elf_ex, &arch_state);
-	if (elf_read_implies_exec(loc->elf_ex, executable_stack))
+	SET_PERSONALITY2(*elf_ex, &arch_state);
+	if (elf_read_implies_exec(*elf_ex, executable_stack))
 		current->personality |= READ_IMPLIES_EXEC;
 
 	if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
@@ -878,7 +877,7 @@  out_free_interp:
 	/* Now we do a little grungy work by mmapping the ELF image into
 	   the correct location in memory. */
 	for(i = 0, elf_ppnt = elf_phdata;
-	    i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
+	    i < elf_ex->e_phnum; i++, elf_ppnt++) {
 		int elf_prot, elf_flags;
 		unsigned long k, vaddr;
 		unsigned long total_size = 0;
@@ -922,9 +921,9 @@  out_free_interp:
 		 * If we are loading ET_EXEC or we have already performed
 		 * the ET_DYN load_addr calculations, proceed normally.
 		 */
-		if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
+		if (elf_ex->e_type == ET_EXEC || load_addr_set) {
 			elf_flags |= MAP_FIXED;
-		} else if (loc->elf_ex.e_type == ET_DYN) {
+		} else if (elf_ex->e_type == ET_DYN) {
 			/*
 			 * This logic is run once for the first LOAD Program
 			 * Header for ET_DYN binaries to calculate the
@@ -973,7 +972,7 @@  out_free_interp:
 			load_bias = ELF_PAGESTART(load_bias - vaddr);
 
 			total_size = total_mapping_size(elf_phdata,
-							loc->elf_ex.e_phnum);
+							elf_ex->e_phnum);
 			if (!total_size) {
 				retval = -EINVAL;
 				goto out_free_dentry;
@@ -991,7 +990,7 @@  out_free_interp:
 		if (!load_addr_set) {
 			load_addr_set = 1;
 			load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
-			if (loc->elf_ex.e_type == ET_DYN) {
+			if (elf_ex->e_type == ET_DYN) {
 				load_bias += error -
 				             ELF_PAGESTART(load_bias + vaddr);
 				load_addr += load_bias;
@@ -1032,7 +1031,7 @@  out_free_interp:
 		}
 	}
 
-	loc->elf_ex.e_entry += load_bias;
+	e_entry = elf_ex->e_entry + load_bias;
 	elf_bss += load_bias;
 	elf_brk += load_bias;
 	start_code += load_bias;
@@ -1075,7 +1074,7 @@  out_free_interp:
 		allow_write_access(interpreter);
 		fput(interpreter);
 	} else {
-		elf_entry = loc->elf_ex.e_entry;
+		elf_entry = e_entry;
 		if (BAD_ADDR(elf_entry)) {
 			retval = -EINVAL;
 			goto out_free_dentry;
@@ -1093,8 +1092,8 @@  out_free_interp:
 		goto out;
 #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
 
-	retval = create_elf_tables(bprm, &loc->elf_ex,
-			  load_addr, interp_load_addr);
+	retval = create_elf_tables(bprm, elf_ex,
+			  load_addr, interp_load_addr, e_entry);
 	if (retval < 0)
 		goto out;
 	current->mm->end_code = end_code;
@@ -1112,7 +1111,7 @@  out_free_interp:
 		 * growing down), and into the unused ELF_ET_DYN_BASE region.
 		 */
 		if (IS_ENABLED(CONFIG_ARCH_HAS_ELF_RANDOMIZE) &&
-		    loc->elf_ex.e_type == ET_DYN && !interpreter)
+		    elf_ex->e_type == ET_DYN && !interpreter)
 			current->mm->brk = current->mm->start_brk =
 				ELF_ET_DYN_BASE;