|
@@ -66,6 +66,12 @@ char core_pattern[CORENAME_MAX_SIZE] = "core";
|
|
unsigned int core_pipe_limit;
|
|
unsigned int core_pipe_limit;
|
|
int suid_dumpable = 0;
|
|
int suid_dumpable = 0;
|
|
|
|
|
|
|
|
+struct core_name {
|
|
|
|
+ char *corename;
|
|
|
|
+ int used, size;
|
|
|
|
+};
|
|
|
|
+static atomic_t call_count = ATOMIC_INIT(1);
|
|
|
|
+
|
|
/* The maximal length of core_pattern is also specified in sysctl.c */
|
|
/* The maximal length of core_pattern is also specified in sysctl.c */
|
|
|
|
|
|
static LIST_HEAD(formats);
|
|
static LIST_HEAD(formats);
|
|
@@ -1459,127 +1465,148 @@ void set_binfmt(struct linux_binfmt *new)
|
|
|
|
|
|
EXPORT_SYMBOL(set_binfmt);
|
|
EXPORT_SYMBOL(set_binfmt);
|
|
|
|
|
|
|
|
+static int expand_corename(struct core_name *cn)
|
|
|
|
+{
|
|
|
|
+ char *old_corename = cn->corename;
|
|
|
|
+
|
|
|
|
+ cn->size = CORENAME_MAX_SIZE * atomic_inc_return(&call_count);
|
|
|
|
+ cn->corename = krealloc(old_corename, cn->size, GFP_KERNEL);
|
|
|
|
+
|
|
|
|
+ if (!cn->corename) {
|
|
|
|
+ kfree(old_corename);
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int cn_printf(struct core_name *cn, const char *fmt, ...)
|
|
|
|
+{
|
|
|
|
+ char *cur;
|
|
|
|
+ int need;
|
|
|
|
+ int ret;
|
|
|
|
+ va_list arg;
|
|
|
|
+
|
|
|
|
+ va_start(arg, fmt);
|
|
|
|
+ need = vsnprintf(NULL, 0, fmt, arg);
|
|
|
|
+ va_end(arg);
|
|
|
|
+
|
|
|
|
+ if (likely(need < cn->size - cn->used - 1))
|
|
|
|
+ goto out_printf;
|
|
|
|
+
|
|
|
|
+ ret = expand_corename(cn);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto expand_fail;
|
|
|
|
+
|
|
|
|
+out_printf:
|
|
|
|
+ cur = cn->corename + cn->used;
|
|
|
|
+ va_start(arg, fmt);
|
|
|
|
+ vsnprintf(cur, need + 1, fmt, arg);
|
|
|
|
+ va_end(arg);
|
|
|
|
+ cn->used += need;
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+expand_fail:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/* format_corename will inspect the pattern parameter, and output a
|
|
/* format_corename will inspect the pattern parameter, and output a
|
|
* name into corename, which must have space for at least
|
|
* name into corename, which must have space for at least
|
|
* CORENAME_MAX_SIZE bytes plus one byte for the zero terminator.
|
|
* CORENAME_MAX_SIZE bytes plus one byte for the zero terminator.
|
|
*/
|
|
*/
|
|
-static int format_corename(char *corename, long signr)
|
|
|
|
|
|
+static int format_corename(struct core_name *cn, long signr)
|
|
{
|
|
{
|
|
const struct cred *cred = current_cred();
|
|
const struct cred *cred = current_cred();
|
|
const char *pat_ptr = core_pattern;
|
|
const char *pat_ptr = core_pattern;
|
|
int ispipe = (*pat_ptr == '|');
|
|
int ispipe = (*pat_ptr == '|');
|
|
- char *out_ptr = corename;
|
|
|
|
- char *const out_end = corename + CORENAME_MAX_SIZE;
|
|
|
|
- int rc;
|
|
|
|
int pid_in_pattern = 0;
|
|
int pid_in_pattern = 0;
|
|
|
|
+ int err = 0;
|
|
|
|
+
|
|
|
|
+ cn->size = CORENAME_MAX_SIZE * atomic_read(&call_count);
|
|
|
|
+ cn->corename = kmalloc(cn->size, GFP_KERNEL);
|
|
|
|
+ cn->used = 0;
|
|
|
|
+
|
|
|
|
+ if (!cn->corename)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
/* Repeat as long as we have more pattern to process and more output
|
|
/* Repeat as long as we have more pattern to process and more output
|
|
space */
|
|
space */
|
|
while (*pat_ptr) {
|
|
while (*pat_ptr) {
|
|
if (*pat_ptr != '%') {
|
|
if (*pat_ptr != '%') {
|
|
- if (out_ptr == out_end)
|
|
|
|
|
|
+ if (*pat_ptr == 0)
|
|
goto out;
|
|
goto out;
|
|
- *out_ptr++ = *pat_ptr++;
|
|
|
|
|
|
+ err = cn_printf(cn, "%c", *pat_ptr++);
|
|
} else {
|
|
} else {
|
|
switch (*++pat_ptr) {
|
|
switch (*++pat_ptr) {
|
|
|
|
+ /* single % at the end, drop that */
|
|
case 0:
|
|
case 0:
|
|
goto out;
|
|
goto out;
|
|
/* Double percent, output one percent */
|
|
/* Double percent, output one percent */
|
|
case '%':
|
|
case '%':
|
|
- if (out_ptr == out_end)
|
|
|
|
- goto out;
|
|
|
|
- *out_ptr++ = '%';
|
|
|
|
|
|
+ err = cn_printf(cn, "%c", '%');
|
|
break;
|
|
break;
|
|
/* pid */
|
|
/* pid */
|
|
case 'p':
|
|
case 'p':
|
|
pid_in_pattern = 1;
|
|
pid_in_pattern = 1;
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- "%d", task_tgid_vnr(current));
|
|
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
|
|
+ err = cn_printf(cn, "%d",
|
|
|
|
+ task_tgid_vnr(current));
|
|
break;
|
|
break;
|
|
/* uid */
|
|
/* uid */
|
|
case 'u':
|
|
case 'u':
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- "%d", cred->uid);
|
|
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
|
|
+ err = cn_printf(cn, "%d", cred->uid);
|
|
break;
|
|
break;
|
|
/* gid */
|
|
/* gid */
|
|
case 'g':
|
|
case 'g':
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- "%d", cred->gid);
|
|
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
|
|
+ err = cn_printf(cn, "%d", cred->gid);
|
|
break;
|
|
break;
|
|
/* signal that caused the coredump */
|
|
/* signal that caused the coredump */
|
|
case 's':
|
|
case 's':
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- "%ld", signr);
|
|
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
|
|
+ err = cn_printf(cn, "%ld", signr);
|
|
break;
|
|
break;
|
|
/* UNIX time of coredump */
|
|
/* UNIX time of coredump */
|
|
case 't': {
|
|
case 't': {
|
|
struct timeval tv;
|
|
struct timeval tv;
|
|
do_gettimeofday(&tv);
|
|
do_gettimeofday(&tv);
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- "%lu", tv.tv_sec);
|
|
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
|
|
+ err = cn_printf(cn, "%lu", tv.tv_sec);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
/* hostname */
|
|
/* hostname */
|
|
case 'h':
|
|
case 'h':
|
|
down_read(&uts_sem);
|
|
down_read(&uts_sem);
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- "%s", utsname()->nodename);
|
|
|
|
|
|
+ err = cn_printf(cn, "%s",
|
|
|
|
+ utsname()->nodename);
|
|
up_read(&uts_sem);
|
|
up_read(&uts_sem);
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
break;
|
|
break;
|
|
/* executable */
|
|
/* executable */
|
|
case 'e':
|
|
case 'e':
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- "%s", current->comm);
|
|
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
|
|
+ err = cn_printf(cn, "%s", current->comm);
|
|
break;
|
|
break;
|
|
/* core limit size */
|
|
/* core limit size */
|
|
case 'c':
|
|
case 'c':
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- "%lu", rlimit(RLIMIT_CORE));
|
|
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
|
|
+ err = cn_printf(cn, "%lu",
|
|
|
|
+ rlimit(RLIMIT_CORE));
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
++pat_ptr;
|
|
++pat_ptr;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
+
|
|
/* Backward compatibility with core_uses_pid:
|
|
/* Backward compatibility with core_uses_pid:
|
|
*
|
|
*
|
|
* If core_pattern does not include a %p (as is the default)
|
|
* If core_pattern does not include a %p (as is the default)
|
|
* and core_uses_pid is set, then .%pid will be appended to
|
|
* and core_uses_pid is set, then .%pid will be appended to
|
|
* the filename. Do not do this for piped commands. */
|
|
* the filename. Do not do this for piped commands. */
|
|
if (!ispipe && !pid_in_pattern && core_uses_pid) {
|
|
if (!ispipe && !pid_in_pattern && core_uses_pid) {
|
|
- rc = snprintf(out_ptr, out_end - out_ptr,
|
|
|
|
- ".%d", task_tgid_vnr(current));
|
|
|
|
- if (rc > out_end - out_ptr)
|
|
|
|
- goto out;
|
|
|
|
- out_ptr += rc;
|
|
|
|
|
|
+ err = cn_printf(cn, ".%d", task_tgid_vnr(current));
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
out:
|
|
out:
|
|
- *out_ptr = 0;
|
|
|
|
return ispipe;
|
|
return ispipe;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1856,7 +1883,7 @@ static int umh_pipe_setup(struct subprocess_info *info)
|
|
void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
|
void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
|
{
|
|
{
|
|
struct core_state core_state;
|
|
struct core_state core_state;
|
|
- char corename[CORENAME_MAX_SIZE + 1];
|
|
|
|
|
|
+ struct core_name cn;
|
|
struct mm_struct *mm = current->mm;
|
|
struct mm_struct *mm = current->mm;
|
|
struct linux_binfmt * binfmt;
|
|
struct linux_binfmt * binfmt;
|
|
const struct cred *old_cred;
|
|
const struct cred *old_cred;
|
|
@@ -1911,7 +1938,13 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
|
*/
|
|
*/
|
|
clear_thread_flag(TIF_SIGPENDING);
|
|
clear_thread_flag(TIF_SIGPENDING);
|
|
|
|
|
|
- ispipe = format_corename(corename, signr);
|
|
|
|
|
|
+ ispipe = format_corename(&cn, signr);
|
|
|
|
+
|
|
|
|
+ if (ispipe == -ENOMEM) {
|
|
|
|
+ printk(KERN_WARNING "format_corename failed\n");
|
|
|
|
+ printk(KERN_WARNING "Aborting core\n");
|
|
|
|
+ goto fail_corename;
|
|
|
|
+ }
|
|
|
|
|
|
if (ispipe) {
|
|
if (ispipe) {
|
|
int dump_count;
|
|
int dump_count;
|
|
@@ -1948,7 +1981,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
|
goto fail_dropcount;
|
|
goto fail_dropcount;
|
|
}
|
|
}
|
|
|
|
|
|
- helper_argv = argv_split(GFP_KERNEL, corename+1, NULL);
|
|
|
|
|
|
+ helper_argv = argv_split(GFP_KERNEL, cn.corename+1, NULL);
|
|
if (!helper_argv) {
|
|
if (!helper_argv) {
|
|
printk(KERN_WARNING "%s failed to allocate memory\n",
|
|
printk(KERN_WARNING "%s failed to allocate memory\n",
|
|
__func__);
|
|
__func__);
|
|
@@ -1961,7 +1994,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
|
argv_free(helper_argv);
|
|
argv_free(helper_argv);
|
|
if (retval) {
|
|
if (retval) {
|
|
printk(KERN_INFO "Core dump to %s pipe failed\n",
|
|
printk(KERN_INFO "Core dump to %s pipe failed\n",
|
|
- corename);
|
|
|
|
|
|
+ cn.corename);
|
|
goto close_fail;
|
|
goto close_fail;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
@@ -1970,7 +2003,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
|
|
if (cprm.limit < binfmt->min_coredump)
|
|
if (cprm.limit < binfmt->min_coredump)
|
|
goto fail_unlock;
|
|
goto fail_unlock;
|
|
|
|
|
|
- cprm.file = filp_open(corename,
|
|
|
|
|
|
+ cprm.file = filp_open(cn.corename,
|
|
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
|
|
O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
|
|
0600);
|
|
0600);
|
|
if (IS_ERR(cprm.file))
|
|
if (IS_ERR(cprm.file))
|
|
@@ -2012,6 +2045,8 @@ fail_dropcount:
|
|
if (ispipe)
|
|
if (ispipe)
|
|
atomic_dec(&core_dump_count);
|
|
atomic_dec(&core_dump_count);
|
|
fail_unlock:
|
|
fail_unlock:
|
|
|
|
+ kfree(cn.corename);
|
|
|
|
+fail_corename:
|
|
coredump_finish(mm);
|
|
coredump_finish(mm);
|
|
revert_creds(old_cred);
|
|
revert_creds(old_cred);
|
|
fail_creds:
|
|
fail_creds:
|