Skip to content

Commit 637e8db

Browse files
Anthony Yznagajfvogel
Anthony Yznaga
authored andcommitted
exec, elf: require opt-in for accepting preserved mem
Don't copy preserved VMAs to the binary being exec'd unless the binary has a "preserved-mem-ok" ELF note. Orabug: 32387884 Signed-off-by: Anthony Yznaga <anthony.yznaga@oracle.com> Reviewed-by: Mike Kravetz <mike.kravetz@oracle.com> (cherry picked from commit aeea589) Signed-off-by: Jack Vogel <jack.vogel@oracle.com>
1 parent c8326d0 commit 637e8db

File tree

3 files changed

+109
-7
lines changed

3 files changed

+109
-7
lines changed

fs/binfmt_elf.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,82 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
678678
* libraries. There is no binary dependent code anywhere else.
679679
*/
680680

681+
static int get_elf_notes(struct linux_binprm *bprm, struct elf_phdr *phdr, char **notes, size_t *notes_sz)
682+
{
683+
char *data;
684+
size_t datasz;
685+
loff_t pos;
686+
int ret;
687+
688+
if (!phdr)
689+
return 0;
690+
691+
datasz = phdr->p_filesz;
692+
if ((datasz > MAX_FILE_NOTE_SIZE) || (datasz < sizeof(struct elf_note)))
693+
return -ENOEXEC;
694+
695+
data = kvmalloc(datasz, GFP_KERNEL);
696+
if (!data)
697+
return -ENOMEM;
698+
699+
pos = phdr->p_offset;
700+
ret = kernel_read(bprm->file, data, datasz, &pos);
701+
if (ret != datasz) {
702+
if (ret >= 0)
703+
ret = -EIO;
704+
kvfree(data);
705+
return ret;
706+
}
707+
708+
*notes = data;
709+
*notes_sz = datasz;
710+
return 0;
711+
}
712+
713+
#define PRESERVED_MEM_OK_STRING "preserved-mem-ok"
714+
#define SZ_PRESERVED_MEM_OK_STRING sizeof(PRESERVED_MEM_OK_STRING)
715+
716+
static int check_preserved_mem_ok(struct linux_binprm *bprm, const char *data, const size_t datasz)
717+
{
718+
size_t off = 0;
719+
size_t remain;
720+
721+
if (!data)
722+
return 0;
723+
724+
while (off < datasz) {
725+
const struct elf_note *nhdr;
726+
const char *name;
727+
728+
remain = datasz - off;
729+
730+
if (remain < sizeof(*nhdr))
731+
return -ENOEXEC;
732+
733+
nhdr = (struct elf_note *)(data + off);
734+
off += sizeof(*nhdr);
735+
remain -= sizeof(*nhdr);
736+
737+
if (nhdr->n_type != 0x07c1feed) {
738+
off += roundup(nhdr->n_namesz, 4) + roundup(nhdr->n_descsz, 4);
739+
continue;
740+
}
741+
742+
if (nhdr->n_namesz > SZ_PRESERVED_MEM_OK_STRING)
743+
return -ENOEXEC;
744+
745+
name = data + off;
746+
if (remain < SZ_PRESERVED_MEM_OK_STRING ||
747+
strncmp(name, PRESERVED_MEM_OK_STRING, SZ_PRESERVED_MEM_OK_STRING))
748+
return -ENOEXEC;
749+
750+
bprm->accepts_preserved_mem = 1;
751+
break;
752+
}
753+
754+
return 0;
755+
}
756+
681757
#define MAX_RSVD_VA_RANGES 64
682758
#define RSVD_VA_STRING "Reserved VA"
683759
#define SZ_RSVD_VA_STRING sizeof(RSVD_VA_STRING)
@@ -784,6 +860,9 @@ static int load_elf_binary(struct linux_binprm *bprm)
784860
int load_addr_set = 0;
785861
unsigned long error;
786862
struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
863+
struct elf_phdr *elf_notes_phdata = NULL;
864+
char *elf_notes = NULL;
865+
size_t elf_notes_sz = 0;
787866
unsigned long elf_bss, elf_brk;
788867
int bss_prot = 0;
789868
int retval, i;
@@ -899,6 +978,10 @@ static int load_elf_binary(struct linux_binprm *bprm)
899978
executable_stack = EXSTACK_DISABLE_X;
900979
break;
901980

981+
case PT_NOTE:
982+
elf_notes_phdata = elf_ppnt;
983+
break;
984+
902985
case PT_LOPROC ... PT_HIPROC:
903986
retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
904987
bprm->file, false,
@@ -950,6 +1033,14 @@ static int load_elf_binary(struct linux_binprm *bprm)
9501033
if (retval)
9511034
goto out_free_dentry;
9521035

1036+
retval = get_elf_notes(bprm, elf_notes_phdata, &elf_notes, &elf_notes_sz);
1037+
if (retval)
1038+
goto out_free_dentry;
1039+
1040+
retval = check_preserved_mem_ok(bprm, elf_notes, elf_notes_sz);
1041+
if (retval)
1042+
goto out_free_dentry;
1043+
9531044
/* Flush all traces of the currently running executable */
9541045
retval = flush_old_exec(bprm);
9551046
if (retval)
@@ -1226,6 +1317,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
12261317
}
12271318
}
12281319

1320+
kvfree(elf_notes);
12291321
kfree(interp_elf_phdata);
12301322
kfree(elf_phdata);
12311323

@@ -1306,6 +1398,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
13061398
if (interpreter)
13071399
fput(interpreter);
13081400
out_free_ph:
1401+
kvfree(elf_notes);
13091402
kfree(elf_phdata);
13101403
goto out;
13111404
}

fs/exec.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,10 +1025,11 @@ static int vma_dup_some(struct mm_struct *old_mm, struct mm_struct *new_mm)
10251025
* On success, this function returns with exec_update_lock
10261026
* held for writing.
10271027
*/
1028-
static int exec_mmap(struct mm_struct *mm)
1028+
static int exec_mmap(struct linux_binprm *bprm)
10291029
{
10301030
struct task_struct *tsk;
10311031
struct mm_struct *old_mm, *active_mm;
1032+
struct mm_struct *mm = bprm->mm;
10321033
int ret;
10331034

10341035
/* Notify parent that we're no longer interested in the old VM */
@@ -1054,11 +1055,13 @@ static int exec_mmap(struct mm_struct *mm)
10541055
up_write(&tsk->signal->exec_update_lock);
10551056
return -EINTR;
10561057
}
1057-
ret = vma_dup_some(old_mm, mm);
1058-
if (ret) {
1059-
up_read(&old_mm->mmap_sem);
1060-
up_write(&tsk->signal->exec_update_lock);
1061-
return ret;
1058+
if (bprm->accepts_preserved_mem) {
1059+
ret = vma_dup_some(old_mm, mm);
1060+
if (ret) {
1061+
up_read(&old_mm->mmap_sem);
1062+
up_write(&tsk->signal->exec_update_lock);
1063+
return ret;
1064+
}
10621065
}
10631066
}
10641067

@@ -1324,7 +1327,7 @@ int flush_old_exec(struct linux_binprm * bprm)
13241327
* Release all of the old mmap stuff
13251328
*/
13261329
acct_arg_size(bprm, 0);
1327-
retval = exec_mmap(bprm->mm);
1330+
retval = exec_mmap(bprm);
13281331
if (retval)
13291332
goto out;
13301333

include/linux/binfmts.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ struct linux_binprm {
5959
#ifdef __alpha__
6060
unsigned int taso:1;
6161
#endif
62+
/*
63+
* Set if the binary being exec'd will accept memory marked
64+
* for preservation by the outgoing process.
65+
*/
66+
UEK_KABI_FILL_HOLE(unsigned int accepts_preserved_mem:1)
67+
6268
unsigned int recursion_depth; /* only for search_binary_handler() */
6369
struct file * file;
6470
struct cred *cred; /* new credentials */

0 commit comments

Comments
 (0)