2009-11-18 03:55:25

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip 0/2] Fix mm->flags consistency issue in coredump


Hi,

These patches are for fixing coredump mm->flags consistency issue.

---
1787 if (mm->core_state || !get_dumpable(mm)) { <- (1)
1788 up_write(&mm->mmap_sem);
1789 put_cred(cred);
1790 goto fail;
1791 }
1792
[...]
1798 if (get_dumpable(mm) == 2) { /* Setuid core dump mode */ <-(2)
1799 flag = O_EXCL; /* Stop rewrite attacks */
1800 cred->fsuid = 0; /* Dump root private */
1801 }
---

Since dumpable bits are not protected by lock, there is a
chance to change these bits between (1) and (2).

To solve this issue, this patch copies mm->flags to
coredump_params.mm_flags at the beginning of do_coredump() and uses it instead of get_dumpable() while dumping core.
This series also introduce coredump parameter structure
for simplify bimfmt->core_dump interface.

Thank you,

---

Masami Hiramatsu (2):
Pass mm->flags as a coredump parameter for consistency
mm: Introduce coredump parameter structure


fs/binfmt_aout.c | 13 ++++++-----
fs/binfmt_elf.c | 50 +++++++++++++++++++----------------------
fs/binfmt_elf_fdpic.c | 38 ++++++++++++-------------------
fs/binfmt_flat.c | 6 ++---
fs/binfmt_som.c | 2 +-
fs/exec.c | 58 ++++++++++++++++++++++++++++++-----------------
include/linux/binfmts.h | 11 ++++++++-
7 files changed, 97 insertions(+), 81 deletions(-)

--
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: [email protected]


2009-11-17 23:54:21

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip 1/2] mm: Introduce coredump parameter structure

Introduce coredump parameter data structure (struct coredump_params)
for simplifying binfmt->core_dump() arguments.
This also cleanup DUMP_WRITE() in elf_core_dump() by style issue.

Signed-off-by: Masami Hiramatsu <[email protected]>
Suggested-by: Ingo Molnar <[email protected]>
Cc: Hidehiro Kawai <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Oleg Nesterov <[email protected]>
Cc: Roland McGrath <[email protected]>
---

fs/binfmt_aout.c | 13 +++++++------
fs/binfmt_elf.c | 38 ++++++++++++++++++++++----------------
fs/binfmt_elf_fdpic.c | 28 ++++++++++++++--------------
fs/binfmt_flat.c | 6 +++---
fs/binfmt_som.c | 2 +-
fs/exec.c | 38 +++++++++++++++++++++-----------------
include/linux/binfmts.h | 10 +++++++++-
7 files changed, 77 insertions(+), 58 deletions(-)

diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
index b639dcf..346b694 100644
--- a/fs/binfmt_aout.c
+++ b/fs/binfmt_aout.c
@@ -32,7 +32,7 @@

static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
static int load_aout_library(struct file*);
-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int aout_core_dump(struct coredump_params *cprm);

static struct linux_binfmt aout_format = {
.module = THIS_MODULE,
@@ -89,8 +89,9 @@ if (file->f_op->llseek) { \
* dumping of the process results in another error..
*/

-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int aout_core_dump(struct coredump_params *cprm)
{
+ struct file *file = cprm->file;
mm_segment_t fs;
int has_dumped = 0;
unsigned long dump_start, dump_size;
@@ -108,16 +109,16 @@ static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, u
current->flags |= PF_DUMPCORE;
strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm));
dump.u_ar0 = offsetof(struct user, regs);
- dump.signal = signr;
- aout_dump_thread(regs, &dump);
+ dump.signal = cprm->signr;
+ aout_dump_thread(cprm->regs, &dump);

/* If the size of the dump file exceeds the rlimit, then see what would happen
if we wrote the stack, but not the data area. */
- if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > limit)
+ if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > cprm->limit)
dump.u_dsize = 0;

/* Make sure we have enough room to write the stack and data areas. */
- if ((dump.u_ssize + 1) * PAGE_SIZE > limit)
+ if ((dump.u_ssize + 1) * PAGE_SIZE > cprm->limit)
dump.u_ssize = 0;

/* make sure we actually have a data and stack area to dump */
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index b9b3bb5..5e27303 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -45,7 +45,7 @@ static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *,
* don't even try.
*/
#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int elf_core_dump(struct coredump_params *cprm);
#else
#define elf_core_dump NULL
#endif
@@ -1277,10 +1277,6 @@ static int writenote(struct memelfnote *men, struct file *file,
}
#undef DUMP_WRITE

-#define DUMP_WRITE(addr, nr) \
- if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
- goto end_coredump;
-
static void fill_elf_header(struct elfhdr *elf, int segs,
u16 machine, u32 flags, u8 osabi)
{
@@ -1906,7 +1902,7 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
* and then they are actually written out. If we run out of core limit
* we just truncate.
*/
-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int elf_core_dump(struct coredump_params *cprm)
{
int has_dumped = 0;
mm_segment_t fs;
@@ -1952,7 +1948,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
* notes. This also sets up the file header.
*/
if (!fill_note_info(elf, segs + 1, /* including notes section */
- &info, signr, regs))
+ &info, cprm->signr, cprm->regs))
goto cleanup;

has_dumped = 1;
@@ -1961,7 +1957,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
fs = get_fs();
set_fs(KERNEL_DS);

- DUMP_WRITE(elf, sizeof(*elf));
+ size += sizeof(*elf);
+ if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf)))
+ goto end_coredump;
+
offset += sizeof(*elf); /* Elf header */
offset += (segs + 1) * sizeof(struct elf_phdr); /* Program headers */
foffset = offset;
@@ -1975,7 +1974,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un

fill_elf_note_phdr(&phdr, sz, offset);
offset += sz;
- DUMP_WRITE(&phdr, sizeof(phdr));
+ size += sizeof(phdr);
+ if (size > cprm->limit ||
+ !dump_write(cprm->file, &phdr, sizeof(phdr)))
+ goto end_coredump;
}

dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
@@ -2006,7 +2008,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
phdr.p_flags |= PF_X;
phdr.p_align = ELF_EXEC_PAGESIZE;

- DUMP_WRITE(&phdr, sizeof(phdr));
+ size += sizeof(phdr);
+ if (size > cprm->limit ||
+ !dump_write(cprm->file, &phdr, sizeof(phdr)))
+ goto end_coredump;
}

#ifdef ELF_CORE_WRITE_EXTRA_PHDRS
@@ -2014,14 +2019,14 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
#endif

/* write out the notes section */
- if (!write_note_info(&info, file, &foffset))
+ if (!write_note_info(&info, cprm->file, &foffset))
goto end_coredump;

- if (elf_coredump_extra_notes_write(file, &foffset))
+ if (elf_coredump_extra_notes_write(cprm->file, &foffset))
goto end_coredump;

/* Align to page */
- if (!dump_seek(file, dataoff - foffset))
+ if (!dump_seek(cprm->file, dataoff - foffset))
goto end_coredump;

for (vma = first_vma(current, gate_vma); vma != NULL;
@@ -2038,12 +2043,13 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
page = get_dump_page(addr);
if (page) {
void *kaddr = kmap(page);
- stop = ((size += PAGE_SIZE) > limit) ||
- !dump_write(file, kaddr, PAGE_SIZE);
+ stop = ((size += PAGE_SIZE) > cprm->limit) ||
+ !dump_write(cprm->file, kaddr,
+ PAGE_SIZE);
kunmap(page);
page_cache_release(page);
} else
- stop = !dump_seek(file, PAGE_SIZE);
+ stop = !dump_seek(cprm->file, PAGE_SIZE);
if (stop)
goto end_coredump;
}
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 38502c6..e65ab9d 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -76,7 +76,7 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *,
struct file *, struct mm_struct *);

#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
-static int elf_fdpic_core_dump(long, struct pt_regs *, struct file *, unsigned long limit);
+static int elf_fdpic_core_dump(struct coredump_params *cprm);
#endif

static struct linux_binfmt elf_fdpic_format = {
@@ -1581,8 +1581,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
* and then they are actually written out. If we run out of core limit
* we just truncate.
*/
-static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
- struct file *file, unsigned long limit)
+static int elf_fdpic_core_dump(struct coredump_params *cprm)
{
#define NUM_NOTES 6
int has_dumped = 0;
@@ -1641,7 +1640,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
goto cleanup;
#endif

- if (signr) {
+ if (cprm->signr) {
struct core_thread *ct;
struct elf_thread_status *tmp;

@@ -1660,14 +1659,14 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
int sz;

tmp = list_entry(t, struct elf_thread_status, list);
- sz = elf_dump_thread_status(signr, tmp);
+ sz = elf_dump_thread_status(cprm->signr, tmp);
thread_status_size += sz;
}
}

/* now collect the dump for the current */
- fill_prstatus(prstatus, current, signr);
- elf_core_copy_regs(&prstatus->pr_reg, regs);
+ fill_prstatus(prstatus, current, cprm->signr);
+ elf_core_copy_regs(&prstatus->pr_reg, cprm->regs);

segs = current->mm->map_count;
#ifdef ELF_CORE_EXTRA_PHDRS
@@ -1702,7 +1701,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,

/* Try to dump the FPU. */
if ((prstatus->pr_fpvalid =
- elf_core_copy_task_fpregs(current, regs, fpu)))
+ elf_core_copy_task_fpregs(current, cprm->regs, fpu)))
fill_note(notes + numnote++,
"CORE", NT_PRFPREG, sizeof(*fpu), fpu);
#ifdef ELF_CORE_COPY_XFPREGS
@@ -1773,7 +1772,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,

/* write out the notes section */
for (i = 0; i < numnote; i++)
- if (!writenote(notes + i, file))
+ if (!writenote(notes + i, cprm->file))
goto end_coredump;

/* write out the thread status notes section */
@@ -1782,25 +1781,26 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
list_entry(t, struct elf_thread_status, list);

for (i = 0; i < tmp->num_notes; i++)
- if (!writenote(&tmp->notes[i], file))
+ if (!writenote(&tmp->notes[i], cprm->file))
goto end_coredump;
}

- if (!dump_seek(file, dataoff))
+ if (!dump_seek(cprm->file, dataoff))
goto end_coredump;

- if (elf_fdpic_dump_segments(file, &size, &limit, mm_flags) < 0)
+ if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,
+ mm_flags) < 0)
goto end_coredump;

#ifdef ELF_CORE_WRITE_EXTRA_DATA
ELF_CORE_WRITE_EXTRA_DATA;
#endif

- if (file->f_pos != offset) {
+ if (cprm->file->f_pos != offset) {
/* Sanity check */
printk(KERN_WARNING
"elf_core_dump: file->f_pos (%lld) != offset (%lld)\n",
- file->f_pos, offset);
+ cprm->file->f_pos, offset);
}

end_coredump:
diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c
index a279665..d4a00ea 100644
--- a/fs/binfmt_flat.c
+++ b/fs/binfmt_flat.c
@@ -87,7 +87,7 @@ static int load_flat_shared_library(int id, struct lib_info *p);
#endif

static int load_flat_binary(struct linux_binprm *, struct pt_regs * regs);
-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+static int flat_core_dump(struct coredump_params *cprm);

static struct linux_binfmt flat_format = {
.module = THIS_MODULE,
@@ -102,10 +102,10 @@ static struct linux_binfmt flat_format = {
* Currently only a stub-function.
*/

-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int flat_core_dump(struct coredump_params *cprm)
{
printk("Process %s:%d received signr %d and should have core dumped\n",
- current->comm, current->pid, (int) signr);
+ current->comm, current->pid, (int) cprm->signr);
return(1);
}

diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c
index eff74b9..2a9b533 100644
--- a/fs/binfmt_som.c
+++ b/fs/binfmt_som.c
@@ -43,7 +43,7 @@ static int load_som_library(struct file *);
* don't even try.
*/
#if 0
-static int som_core_dump(long signr, struct pt_regs *regs, unsigned long limit);
+static int som_core_dump(struct coredump_params *cprm);
#else
#define som_core_dump NULL
#endif
diff --git a/fs/exec.c b/fs/exec.c
index ba112bd..5daf7d5 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1756,17 +1756,20 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
struct mm_struct *mm = current->mm;
struct linux_binfmt * binfmt;
struct inode * inode;
- struct file * file;
const struct cred *old_cred;
struct cred *cred;
int retval = 0;
int flag = 0;
int ispipe = 0;
- unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
char **helper_argv = NULL;
int helper_argc = 0;
int dump_count = 0;
static atomic_t core_dump_count = ATOMIC_INIT(0);
+ struct coredump_params cprm = {
+ .signr = signr,
+ .regs = regs,
+ .limit = current->signal->rlim[RLIMIT_CORE].rlim_cur,
+ };

audit_core_dumps(signr);

@@ -1822,15 +1825,15 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
ispipe = format_corename(corename, signr);
unlock_kernel();

- if ((!ispipe) && (core_limit < binfmt->min_coredump))
+ if ((!ispipe) && (cprm.limit < binfmt->min_coredump))
goto fail_unlock;

if (ispipe) {
- if (core_limit == 0) {
+ if (cprm.limit == 0) {
/*
* Normally core limits are irrelevant to pipes, since
* we're not writing to the file system, but we use
- * core_limit of 0 here as a speacial value. Any
+ * cprm.limit of 0 here as a speacial value. Any
* non-zero limit gets set to RLIM_INFINITY below, but
* a limit of 0 skips the dump. This is a consistent
* way to catch recursive crashes. We can still crash
@@ -1863,25 +1866,25 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
goto fail_dropcount;
}

- core_limit = RLIM_INFINITY;
+ cprm.limit = RLIM_INFINITY;

/* SIGPIPE can happen, but it's just never processed */
if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
- &file)) {
+ &cprm.file)) {
printk(KERN_INFO "Core dump to %s pipe failed\n",
corename);
goto fail_dropcount;
}
} else
- file = filp_open(corename,
+ cprm.file = filp_open(corename,
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
0600);
- if (IS_ERR(file))
+ if (IS_ERR(cprm.file))
goto fail_dropcount;
- inode = file->f_path.dentry->d_inode;
+ inode = cprm.file->f_path.dentry->d_inode;
if (inode->i_nlink > 1)
goto close_fail; /* multiple links - don't dump */
- if (!ispipe && d_unhashed(file->f_path.dentry))
+ if (!ispipe && d_unhashed(cprm.file->f_path.dentry))
goto close_fail;

/* AK: actually i see no reason to not allow this for named pipes etc.,
@@ -1894,21 +1897,22 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
*/
if (inode->i_uid != current_fsuid())
goto close_fail;
- if (!file->f_op)
+ if (!cprm.file->f_op)
goto close_fail;
- if (!file->f_op->write)
+ if (!cprm.file->f_op->write)
goto close_fail;
- if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
+ if (!ispipe &&
+ do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file) != 0)
goto close_fail;

- retval = binfmt->core_dump(signr, regs, file, core_limit);
+ retval = binfmt->core_dump(&cprm);

if (retval)
current->signal->group_exit_code |= 0x80;
close_fail:
if (ispipe && core_pipe_limit)
- wait_for_dump_helpers(file);
- filp_close(file, NULL);
+ wait_for_dump_helpers(cprm.file);
+ filp_close(cprm.file, NULL);
fail_dropcount:
if (dump_count)
atomic_dec(&core_dump_count);
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index aece486..cd4349b 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -68,6 +68,14 @@ struct linux_binprm{

#define BINPRM_MAX_RECURSION 4

+/* Function parameter for binfmt->coredump */
+struct coredump_params {
+ long signr;
+ struct pt_regs *regs;
+ struct file *file;
+ unsigned long limit;
+};
+
/*
* This structure defines the functions that are used to load the binary formats that
* linux accepts.
@@ -77,7 +85,7 @@ struct linux_binfmt {
struct module *module;
int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
int (*load_shlib)(struct file *);
- int (*core_dump)(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+ int (*core_dump)(struct coredump_params *cprm);
unsigned long min_coredump; /* minimal dump size */
int hasvdso;
};


--
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: [email protected]

2009-11-18 04:19:53

by Masami Hiramatsu

[permalink] [raw]
Subject: [PATCH -tip 2/2] Pass mm->flags as a coredump parameter for consistency

Pass mm->flags as a coredump parameter for consistency.

---
1787 if (mm->core_state || !get_dumpable(mm)) { <- (1)
1788 up_write(&mm->mmap_sem);
1789 put_cred(cred);
1790 goto fail;
1791 }
1792
[...]
1798 if (get_dumpable(mm) == 2) { /* Setuid core dump mode */ <-(2)
1799 flag = O_EXCL; /* Stop rewrite attacks */
1800 cred->fsuid = 0; /* Dump root private */
1801 }
---

Since dumpable bits are not protected by lock, there is a
chance to change these bits between (1) and (2).

To solve this issue, this patch copies mm->flags to
coredump_params.mm_flags at the beginning of do_coredump() and uses it instead of get_dumpable() while dumping core.

This copy is also passed to binfmt->core_dump, since
elf*_core_dump() uses dump_filter bits in mm->flags.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Roland McGrath <[email protected]>
Cc: Hidehiro Kawai <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Oleg Nesterov <[email protected]>
Cc: Ingo Molnar <[email protected]>
---

fs/binfmt_elf.c | 12 ++----------
fs/binfmt_elf_fdpic.c | 12 ++----------
fs/exec.c | 20 ++++++++++++++++----
include/linux/binfmts.h | 1 +
4 files changed, 21 insertions(+), 24 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 5e27303..2f76489 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1911,7 +1911,6 @@ static int elf_core_dump(struct coredump_params *cprm)
struct vm_area_struct *vma, *gate_vma;
struct elfhdr *elf = NULL;
loff_t offset = 0, dataoff, foffset;
- unsigned long mm_flags;
struct elf_note_info info;

/*
@@ -1982,13 +1981,6 @@ static int elf_core_dump(struct coredump_params *cprm)

dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);

- /*
- * We must use the same mm->flags while dumping core to avoid
- * inconsistency between the program headers and bodies, otherwise an
- * unusable core file can be generated.
- */
- mm_flags = current->mm->flags;
-
/* Write program headers for segments dump */
for (vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma)) {
@@ -1998,7 +1990,7 @@ static int elf_core_dump(struct coredump_params *cprm)
phdr.p_offset = offset;
phdr.p_vaddr = vma->vm_start;
phdr.p_paddr = 0;
- phdr.p_filesz = vma_dump_size(vma, mm_flags);
+ phdr.p_filesz = vma_dump_size(vma, cprm->mm_flags);
phdr.p_memsz = vma->vm_end - vma->vm_start;
offset += phdr.p_filesz;
phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
@@ -2034,7 +2026,7 @@ static int elf_core_dump(struct coredump_params *cprm)
unsigned long addr;
unsigned long end;

- end = vma->vm_start + vma_dump_size(vma, mm_flags);
+ end = vma->vm_start + vma_dump_size(vma, cprm->mm_flags);

for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
struct page *page;
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index e65ab9d..28af3e9 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -1604,7 +1604,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
#endif
int thread_status_size = 0;
elf_addr_t *auxv;
- unsigned long mm_flags;

/*
* We no longer stop all VM operations.
@@ -1735,13 +1734,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
/* Page-align dumped data */
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);

- /*
- * We must use the same mm->flags while dumping core to avoid
- * inconsistency between the program headers and bodies, otherwise an
- * unusable core file can be generated.
- */
- mm_flags = current->mm->flags;
-
/* write program headers for segments dump */
for (vma = current->mm->mmap; vma; vma = vma->vm_next) {
struct elf_phdr phdr;
@@ -1753,7 +1745,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
phdr.p_offset = offset;
phdr.p_vaddr = vma->vm_start;
phdr.p_paddr = 0;
- phdr.p_filesz = maydump(vma, mm_flags) ? sz : 0;
+ phdr.p_filesz = maydump(vma, cprm->mm_flags) ? sz : 0;
phdr.p_memsz = sz;
offset += phdr.p_filesz;
phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
@@ -1789,7 +1781,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
goto end_coredump;

if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,
- mm_flags) < 0)
+ cprm->mm_flags) < 0)
goto end_coredump;

#ifdef ELF_CORE_WRITE_EXTRA_DATA
diff --git a/fs/exec.c b/fs/exec.c
index 5daf7d5..2ec6973 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1718,14 +1718,19 @@ void set_dumpable(struct mm_struct *mm, int value)
}
}

-int get_dumpable(struct mm_struct *mm)
+static int __get_dumpable(unsigned long mm_flags)
{
int ret;

- ret = mm->flags & 0x3;
+ ret = mm_flags & MMF_DUMPABLE_MASK;
return (ret >= 2) ? 2 : ret;
}

+int get_dumpable(struct mm_struct *mm)
+{
+ return __get_dumpable(mm->flags);
+}
+
static void wait_for_dump_helpers(struct file *file)
{
struct pipe_inode_info *pipe;
@@ -1769,6 +1774,12 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
.signr = signr,
.regs = regs,
.limit = current->signal->rlim[RLIMIT_CORE].rlim_cur,
+ /*
+ * We must use the same mm->flags while dumping core to avoid
+ * inconsistency of bit flags, since this flag is not protected
+ * by any locks.
+ */
+ .mm_flags = mm->flags,
};

audit_core_dumps(signr);
@@ -1787,7 +1798,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
/*
* If another thread got here first, or we are not dumpable, bail out.
*/
- if (mm->core_state || !get_dumpable(mm)) {
+ if (mm->core_state || !__get_dumpable(cprm.mm_flags)) {
up_write(&mm->mmap_sem);
put_cred(cred);
goto fail;
@@ -1798,7 +1809,8 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
* process nor do we know its entire history. We only know it
* was tainted so we dump it as root in mode 2.
*/
- if (get_dumpable(mm) == 2) { /* Setuid core dump mode */
+ if (__get_dumpable(cprm.mm_flags) == 2) {
+ /* Setuid core dump mode */
flag = O_EXCL; /* Stop rewrite attacks */
cred->fsuid = 0; /* Dump root private */
}
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index cd4349b..99e529b 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -74,6 +74,7 @@ struct coredump_params {
struct pt_regs *regs;
struct file *file;
unsigned long limit;
+ unsigned long mm_flags;
};

/*


--
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: [email protected]

2009-11-18 00:21:03

by Masami Hiramatsu

[permalink] [raw]
Subject: Re: [PATCH -tip 1/2] mm: Introduce coredump parameter structure

Oops, it's not mm, but bimfmts.

Masami Hiramatsu wrote:
> Introduce coredump parameter data structure (struct coredump_params)
> for simplifying binfmt->core_dump() arguments.
> This also cleanup DUMP_WRITE() in elf_core_dump() by style issue.
>
> Signed-off-by: Masami Hiramatsu<[email protected]>
> Suggested-by: Ingo Molnar<[email protected]>
> Cc: Hidehiro Kawai<[email protected]>
> Cc: Andrew Morton<[email protected]>
> Cc: Oleg Nesterov<[email protected]>
> Cc: Roland McGrath<[email protected]>

--
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: [email protected]

2009-11-19 13:30:24

by Hidehiro Kawai

[permalink] [raw]
Subject: Re: [PATCH -tip 2/2] Pass mm->flags as a coredump parameter for consistency

Hi Hiramatsu-san,

Masami Hiramatsu wrote:

>> Pass mm->flags as a coredump parameter for consistency.
>>
>> ---
>> 1787 if (mm->core_state || !get_dumpable(mm)) { <- (1)
>> 1788 up_write(&mm->mmap_sem);
>> 1789 put_cred(cred);
>> 1790 goto fail;
>> 1791 }
>> 1792
>> [...]
>> 1798 if (get_dumpable(mm) == 2) { /* Setuid core dump mode */ <-(2)
>> 1799 flag = O_EXCL; /* Stop rewrite attacks */
>> 1800 cred->fsuid = 0; /* Dump root private */
>> 1801 }
>> ---
>>
>> Since dumpable bits are not protected by lock, there is a
>> chance to change these bits between (1) and (2).

Yes, a race condition can be caused by prctl from another thread.
And your patch is fine with me. Thanks!

Reviewed-by: Hidehiro Kawai <[email protected]>
--
Hidehiro Kawai
Hitachi, Systems Development Laboratory
Linux Technology Center

2009-11-19 15:33:23

by Cong Wang

[permalink] [raw]
Subject: Re: [PATCH -tip 0/2] Fix mm->flags consistency issue in coredump

On Tue, Nov 17, 2009 at 06:53:05PM -0500, Masami Hiramatsu wrote:
>
>Hi,
>
>These patches are for fixing coredump mm->flags consistency issue.
>
>---
>1787 if (mm->core_state || !get_dumpable(mm)) { <- (1)
>1788 up_write(&mm->mmap_sem);
>1789 put_cred(cred);
>1790 goto fail;
>1791 }
>1792
>[...]
>1798 if (get_dumpable(mm) == 2) { /* Setuid core dump mode */ <-(2)
>1799 flag = O_EXCL; /* Stop rewrite attacks */
>1800 cred->fsuid = 0; /* Dump root private */
>1801 }
>---
>
>Since dumpable bits are not protected by lock, there is a
>chance to change these bits between (1) and (2).
>
>To solve this issue, this patch copies mm->flags to
>coredump_params.mm_flags at the beginning of do_coredump() and uses it instead of get_dumpable() while dumping core.
>This series also introduce coredump parameter structure
>for simplify bimfmt->core_dump interface.


So, this patch set hides 'mm_flags' from globally in mm_struct
to locally in do_coredump() function, by copying it to a local
data structure?

Hmm, seems reasonable.

Reviewed-by: WANG Cong <[email protected]>

Thanks.


>
>Thank you,
>
>---
>
>Masami Hiramatsu (2):
> Pass mm->flags as a coredump parameter for consistency
> mm: Introduce coredump parameter structure
>
>
> fs/binfmt_aout.c | 13 ++++++-----
> fs/binfmt_elf.c | 50 +++++++++++++++++++----------------------
> fs/binfmt_elf_fdpic.c | 38 ++++++++++++-------------------
> fs/binfmt_flat.c | 6 ++---
> fs/binfmt_som.c | 2 +-
> fs/exec.c | 58 ++++++++++++++++++++++++++++++-----------------
> include/linux/binfmts.h | 11 ++++++++-
> 7 files changed, 97 insertions(+), 81 deletions(-)
>
>--
>Masami Hiramatsu
>
>Software Engineer
>Hitachi Computer Products (America), Inc.
>Software Solutions Division
>e-mail: [email protected]
>--
>To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>the body of a message to [email protected]
>More majordomo info at http://vger.kernel.org/majordomo-info.html
>Please read the FAQ at http://www.tux.org/lkml/

--
Live like a child, think like the god.