Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/imgact_elf.c
Show All 30 Lines | |||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_capsicum.h" | #include "opt_capsicum.h" | ||||
#include "opt_compat.h" | #include "opt_compat.h" | ||||
#include "opt_gzio.h" | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/capsicum.h> | #include <sys/capsicum.h> | ||||
#include <sys/compressor.h> | |||||
#include <sys/exec.h> | #include <sys/exec.h> | ||||
#include <sys/fcntl.h> | #include <sys/fcntl.h> | ||||
#include <sys/gzio.h> | |||||
#include <sys/imgact.h> | #include <sys/imgact.h> | ||||
#include <sys/imgact_elf.h> | #include <sys/imgact_elf.h> | ||||
#include <sys/jail.h> | #include <sys/jail.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mount.h> | #include <sys/mount.h> | ||||
#include <sys/mman.h> | #include <sys/mman.h> | ||||
▲ Show 20 Lines • Show All 1,126 Lines • ▼ Show 20 Lines | |||||
/* Coredump output parameters. */ | /* Coredump output parameters. */ | ||||
struct coredump_params { | struct coredump_params { | ||||
off_t offset; | off_t offset; | ||||
struct ucred *active_cred; | struct ucred *active_cred; | ||||
struct ucred *file_cred; | struct ucred *file_cred; | ||||
struct thread *td; | struct thread *td; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
struct gzio_stream *gzs; | struct compressor *comp; | ||||
}; | }; | ||||
extern int compress_user_cores; | |||||
extern int compress_user_cores_level; | |||||
static void cb_put_phdr(vm_map_entry_t, void *); | static void cb_put_phdr(vm_map_entry_t, void *); | ||||
static void cb_size_segment(vm_map_entry_t, void *); | static void cb_size_segment(vm_map_entry_t, void *); | ||||
static int core_write(struct coredump_params *, const void *, size_t, off_t, | static int core_write(struct coredump_params *, const void *, size_t, off_t, | ||||
enum uio_seg); | enum uio_seg); | ||||
static void each_dumpable_segment(struct thread *, segment_callback, void *); | static void each_dumpable_segment(struct thread *, segment_callback, void *); | ||||
static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t, | static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t, | ||||
struct note_info_list *, size_t); | struct note_info_list *, size_t); | ||||
static void __elfN(prepare_notes)(struct thread *, struct note_info_list *, | static void __elfN(prepare_notes)(struct thread *, struct note_info_list *, | ||||
Show All 15 Lines | |||||
static void __elfN(note_procstat_psstrings)(void *, struct sbuf *, size_t *); | static void __elfN(note_procstat_psstrings)(void *, struct sbuf *, size_t *); | ||||
static void note_procstat_files(void *, struct sbuf *, size_t *); | static void note_procstat_files(void *, struct sbuf *, size_t *); | ||||
static void note_procstat_groups(void *, struct sbuf *, size_t *); | static void note_procstat_groups(void *, struct sbuf *, size_t *); | ||||
static void note_procstat_osrel(void *, struct sbuf *, size_t *); | static void note_procstat_osrel(void *, struct sbuf *, size_t *); | ||||
static void note_procstat_rlimit(void *, struct sbuf *, size_t *); | static void note_procstat_rlimit(void *, struct sbuf *, size_t *); | ||||
static void note_procstat_umask(void *, struct sbuf *, size_t *); | static void note_procstat_umask(void *, struct sbuf *, size_t *); | ||||
static void note_procstat_vmmap(void *, struct sbuf *, size_t *); | static void note_procstat_vmmap(void *, struct sbuf *, size_t *); | ||||
#ifdef GZIO | |||||
extern int compress_user_cores_gzlevel; | |||||
/* | /* | ||||
* Write out a core segment to the compression stream. | * Write out a core segment to the compression stream. | ||||
*/ | */ | ||||
static int | static int | ||||
compress_chunk(struct coredump_params *p, char *base, char *buf, u_int len) | compress_chunk(struct coredump_params *p, char *base, char *buf, u_int len) | ||||
{ | { | ||||
u_int chunk_len; | u_int chunk_len; | ||||
int error; | int error; | ||||
while (len > 0) { | while (len > 0) { | ||||
chunk_len = MIN(len, CORE_BUF_SIZE); | chunk_len = MIN(len, CORE_BUF_SIZE); | ||||
/* | /* | ||||
* We can get EFAULT error here. | * We can get EFAULT error here. | ||||
* In that case zero out the current chunk of the segment. | * In that case zero out the current chunk of the segment. | ||||
*/ | */ | ||||
error = copyin(base, buf, chunk_len); | error = copyin(base, buf, chunk_len); | ||||
if (error != 0) | if (error != 0) | ||||
bzero(buf, chunk_len); | bzero(buf, chunk_len); | ||||
error = gzio_write(p->gzs, buf, chunk_len); | error = compressor_write(p->comp, buf, chunk_len); | ||||
if (error != 0) | if (error != 0) | ||||
break; | break; | ||||
base += chunk_len; | base += chunk_len; | ||||
len -= chunk_len; | len -= chunk_len; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
core_gz_write(void *base, size_t len, off_t offset, void *arg) | core_compressed_write(void *base, size_t len, off_t offset, void *arg) | ||||
{ | { | ||||
return (core_write((struct coredump_params *)arg, base, len, offset, | return (core_write((struct coredump_params *)arg, base, len, offset, | ||||
UIO_SYSSPACE)); | UIO_SYSSPACE)); | ||||
} | } | ||||
#endif /* GZIO */ | |||||
static int | static int | ||||
core_write(struct coredump_params *p, const void *base, size_t len, | core_write(struct coredump_params *p, const void *base, size_t len, | ||||
off_t offset, enum uio_seg seg) | off_t offset, enum uio_seg seg) | ||||
{ | { | ||||
return (vn_rdwr_inchunks(UIO_WRITE, p->vp, __DECONST(void *, base), | return (vn_rdwr_inchunks(UIO_WRITE, p->vp, __DECONST(void *, base), | ||||
len, offset, seg, IO_UNIT | IO_DIRECT | IO_RANGELOCKED, | len, offset, seg, IO_UNIT | IO_DIRECT | IO_RANGELOCKED, | ||||
p->active_cred, p->file_cred, NULL, p->td)); | p->active_cred, p->file_cred, NULL, p->td)); | ||||
} | } | ||||
static int | static int | ||||
core_output(void *base, size_t len, off_t offset, struct coredump_params *p, | core_output(void *base, size_t len, off_t offset, struct coredump_params *p, | ||||
void *tmpbuf) | void *tmpbuf) | ||||
{ | { | ||||
int error; | int error; | ||||
#ifdef GZIO | if (p->comp != NULL) | ||||
if (p->gzs != NULL) | |||||
return (compress_chunk(p, base, tmpbuf, len)); | return (compress_chunk(p, base, tmpbuf, len)); | ||||
#endif | |||||
/* | /* | ||||
* EFAULT is a non-fatal error that we can get, for example, | * EFAULT is a non-fatal error that we can get, for example, | ||||
* if the segment is backed by a file but extends beyond its | * if the segment is backed by a file but extends beyond its | ||||
* end. | * end. | ||||
*/ | */ | ||||
error = core_write(p, base, len, offset, UIO_USERSPACE); | error = core_write(p, base, len, offset, UIO_USERSPACE); | ||||
if (error == EFAULT) { | if (error == EFAULT) { | ||||
log(LOG_WARNING, "Failed to fully fault in a core file segment " | log(LOG_WARNING, "Failed to fully fault in a core file segment " | ||||
Show All 28 Lines | sbuf_drain_core_output(void *arg, const char *data, int len) | ||||
* non-sleepable lock held is unsafe. The lock is needed for | * non-sleepable lock held is unsafe. The lock is needed for | ||||
* those routines when dumping a live process. In our case we | * those routines when dumping a live process. In our case we | ||||
* can safely release the lock before draining and acquire | * can safely release the lock before draining and acquire | ||||
* again after. | * again after. | ||||
*/ | */ | ||||
locked = PROC_LOCKED(p->td->td_proc); | locked = PROC_LOCKED(p->td->td_proc); | ||||
if (locked) | if (locked) | ||||
PROC_UNLOCK(p->td->td_proc); | PROC_UNLOCK(p->td->td_proc); | ||||
#ifdef GZIO | if (p->comp != NULL) | ||||
if (p->gzs != NULL) | error = compressor_write(p->comp, __DECONST(char *, data), len); | ||||
error = gzio_write(p->gzs, __DECONST(char *, data), len); | |||||
else | else | ||||
#endif | |||||
error = core_write(p, __DECONST(void *, data), len, p->offset, | error = core_write(p, __DECONST(void *, data), len, p->offset, | ||||
UIO_SYSSPACE); | UIO_SYSSPACE); | ||||
if (locked) | if (locked) | ||||
PROC_LOCK(p->td->td_proc); | PROC_LOCK(p->td->td_proc); | ||||
if (error != 0) | if (error != 0) | ||||
return (-error); | return (-error); | ||||
p->offset += len; | p->offset += len; | ||||
return (len); | return (len); | ||||
Show All 18 Lines | __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) | ||||
struct ucred *cred = td->td_ucred; | struct ucred *cred = td->td_ucred; | ||||
int error = 0; | int error = 0; | ||||
struct sseg_closure seginfo; | struct sseg_closure seginfo; | ||||
struct note_info_list notelst; | struct note_info_list notelst; | ||||
struct coredump_params params; | struct coredump_params params; | ||||
struct note_info *ninfo; | struct note_info *ninfo; | ||||
void *hdr, *tmpbuf; | void *hdr, *tmpbuf; | ||||
size_t hdrsize, notesz, coresize; | size_t hdrsize, notesz, coresize; | ||||
#ifdef GZIO | |||||
boolean_t compress; | |||||
compress = (flags & IMGACT_CORE_COMPRESS) != 0; | |||||
#endif | |||||
hdr = NULL; | hdr = NULL; | ||||
tmpbuf = NULL; | tmpbuf = NULL; | ||||
TAILQ_INIT(¬elst); | TAILQ_INIT(¬elst); | ||||
/* Size the program segments. */ | /* Size the program segments. */ | ||||
seginfo.count = 0; | seginfo.count = 0; | ||||
seginfo.size = 0; | seginfo.size = 0; | ||||
each_dumpable_segment(td, cb_size_segment, &seginfo); | each_dumpable_segment(td, cb_size_segment, &seginfo); | ||||
/* | /* | ||||
* Collect info about the core file header area. | * Collect info about the core file header area. | ||||
*/ | */ | ||||
hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); | hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); | ||||
if (seginfo.count + 1 >= PN_XNUM) | if (seginfo.count + 1 >= PN_XNUM) | ||||
hdrsize += sizeof(Elf_Shdr); | hdrsize += sizeof(Elf_Shdr); | ||||
__elfN(prepare_notes)(td, ¬elst, ¬esz); | __elfN(prepare_notes)(td, ¬elst, ¬esz); | ||||
coresize = round_page(hdrsize + notesz) + seginfo.size; | coresize = round_page(hdrsize + notesz) + seginfo.size; | ||||
/* Set up core dump parameters. */ | /* Set up core dump parameters. */ | ||||
params.offset = 0; | params.offset = 0; | ||||
params.active_cred = cred; | params.active_cred = cred; | ||||
params.file_cred = NOCRED; | params.file_cred = NOCRED; | ||||
params.td = td; | params.td = td; | ||||
params.vp = vp; | params.vp = vp; | ||||
params.gzs = NULL; | params.comp = NULL; | ||||
#ifdef RACCT | #ifdef RACCT | ||||
if (racct_enable) { | if (racct_enable) { | ||||
PROC_LOCK(td->td_proc); | PROC_LOCK(td->td_proc); | ||||
error = racct_add(td->td_proc, RACCT_CORE, coresize); | error = racct_add(td->td_proc, RACCT_CORE, coresize); | ||||
PROC_UNLOCK(td->td_proc); | PROC_UNLOCK(td->td_proc); | ||||
if (error != 0) { | if (error != 0) { | ||||
error = EFAULT; | error = EFAULT; | ||||
goto done; | goto done; | ||||
} | } | ||||
} | } | ||||
#endif | #endif | ||||
if (coresize >= limit) { | if (coresize >= limit) { | ||||
error = EFAULT; | error = EFAULT; | ||||
goto done; | goto done; | ||||
} | } | ||||
#ifdef GZIO | |||||
/* Create a compression stream if necessary. */ | /* Create a compression stream if necessary. */ | ||||
if (compress) { | if (compress_user_cores != 0) { | ||||
params.gzs = gzio_init(core_gz_write, GZIO_DEFLATE, | params.comp = compressor_init(core_compressed_write, | ||||
CORE_BUF_SIZE, compress_user_cores_gzlevel, ¶ms); | compress_user_cores, CORE_BUF_SIZE, | ||||
if (params.gzs == NULL) { | compress_user_cores_level, ¶ms); | ||||
if (params.comp == NULL) { | |||||
error = EFAULT; | error = EFAULT; | ||||
goto done; | goto done; | ||||
} | } | ||||
tmpbuf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); | tmpbuf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); | ||||
} | } | ||||
#endif | |||||
/* | /* | ||||
* Allocate memory for building the header, fill it up, | * Allocate memory for building the header, fill it up, | ||||
* and write it out following the notes. | * and write it out following the notes. | ||||
*/ | */ | ||||
hdr = malloc(hdrsize, M_TEMP, M_WAITOK); | hdr = malloc(hdrsize, M_TEMP, M_WAITOK); | ||||
error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst, | error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst, | ||||
notesz); | notesz); | ||||
Show All 9 Lines | if (error == 0) { | ||||
for (i = 0; i < seginfo.count; i++) { | for (i = 0; i < seginfo.count; i++) { | ||||
error = core_output((caddr_t)(uintptr_t)php->p_vaddr, | error = core_output((caddr_t)(uintptr_t)php->p_vaddr, | ||||
php->p_filesz, offset, ¶ms, tmpbuf); | php->p_filesz, offset, ¶ms, tmpbuf); | ||||
if (error != 0) | if (error != 0) | ||||
break; | break; | ||||
offset += php->p_filesz; | offset += php->p_filesz; | ||||
php++; | php++; | ||||
} | } | ||||
#ifdef GZIO | if (error == 0 && params.comp != NULL) | ||||
if (error == 0 && compress) | error = compressor_flush(params.comp); | ||||
error = gzio_flush(params.gzs); | |||||
#endif | |||||
} | } | ||||
if (error) { | if (error) { | ||||
log(LOG_WARNING, | log(LOG_WARNING, | ||||
"Failed to write core file for process %s (error %d)\n", | "Failed to write core file for process %s (error %d)\n", | ||||
curproc->p_comm, error); | curproc->p_comm, error); | ||||
} | } | ||||
done: | done: | ||||
#ifdef GZIO | |||||
if (compress) { | |||||
free(tmpbuf, M_TEMP); | free(tmpbuf, M_TEMP); | ||||
if (params.gzs != NULL) | if (params.comp != NULL) | ||||
gzio_fini(params.gzs); | compressor_fini(params.comp); | ||||
} | |||||
#endif | |||||
while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { | while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { | ||||
TAILQ_REMOVE(¬elst, ninfo, link); | TAILQ_REMOVE(¬elst, ninfo, link); | ||||
free(ninfo, M_TEMP); | free(ninfo, M_TEMP); | ||||
} | } | ||||
if (hdr != NULL) | if (hdr != NULL) | ||||
free(hdr, M_TEMP); | free(hdr, M_TEMP); | ||||
return (error); | return (error); | ||||
▲ Show 20 Lines • Show All 1,008 Lines • Show Last 20 Lines |