pmc: Rework PROCEXEC event to support PIEs

Currently the PROCEXEC event only reports a single address, entryaddr,
which is the entry point of the interpreter in the typical dynamic case,
and used solely to calculate the base address of the interpreter. For
PDEs this is fine, since the base address is known from the program
headers, but for PIEs the base address varies at run time based on where
the kernel chooses to load it, and so pmcstat has no way of knowing the
real address ranges for the executable. This was less of an issue in the
past since PIEs were rare, but now they're on by default on 64-bit
architectures it's more of a problem.

To solve this, pass through what was picked for et_dyn_addr by the
kernel, and use that as the offset for the executable's start address
just as is done for everything in the kernel. Since we're changing this
interface, sanitise the way we determine the interpreter's base address
by passing it through directly rather than indirectly via the entry
point and having to subtract off whatever the ELF header's e_entry is
(and anything that wants the entry point in future can still add that
back on as needed; this merely changes the interface to directly provide
the underlying variables involved).

This will be followed up by a bump to the pmc major version.

Reviewed by:	jhb
Differential Revision:	https://reviews.freebsd.org/D39595
This commit is contained in:
Jessica Clarke 2023-05-31 00:20:36 +01:00
parent 659a0041dd
commit 94426d21bf
12 changed files with 51 additions and 38 deletions

View file

@ -163,9 +163,12 @@ procexec_to_json(struct pmclog_ev *ev)
startent = startentry(ev);
snprintf(eventbuf, sizeof(eventbuf),
"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", "
"\"start\": \"0x%016jx\", \"pathname\": \"%s\"}\n",
"\"base\": \"0x%016jx\", \"dyn\": \"0x%016jx\", "
"\"pathname\": \"%s\"}\n",
startent.c_str(), ev->pl_u.pl_x.pl_pmcid, ev->pl_u.pl_x.pl_pid,
(uintmax_t)ev->pl_u.pl_x.pl_entryaddr, ev->pl_u.pl_x.pl_pathname);
(uintmax_t)ev->pl_u.pl_x.pl_baseaddr,
(uintmax_t)ev->pl_u.pl_x.pl_dynaddr,
ev->pl_u.pl_x.pl_pathname);
return string(eventbuf);
}

View file

@ -393,7 +393,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_procexec);
PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pid);
PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pmcid);
PMCLOG_READADDR(le,ev->pl_u.pl_x.pl_entryaddr);
PMCLOG_READADDR(le,ev->pl_u.pl_x.pl_baseaddr);
PMCLOG_READADDR(le,ev->pl_u.pl_x.pl_dynaddr);
PMCLOG_READSTRING(le,ev->pl_u.pl_x.pl_pathname,pathlen);
break;
case PMCLOG_TYPE_PROCEXIT:

View file

@ -132,7 +132,8 @@ struct pmclog_ev_proccreate {
struct pmclog_ev_procexec {
pid_t pl_pid;
pmc_id_t pl_pmcid;
uintfptr_t pl_entryaddr;
uintptr_t pl_baseaddr;
uintptr_t pl_dynaddr;
char pl_pathname[PATH_MAX];
};

View file

@ -336,7 +336,7 @@ struct pmcstat_image *
int pmcstat_string_lookup_hash(pmcstat_interned_string _is);
void pmcstat_process_elf_exec(struct pmcstat_process *_pp,
struct pmcstat_image *_image, uintfptr_t _entryaddr,
struct pmcstat_image *_image, uintptr_t _baseaddr, uintptr_t _dynaddr,
struct pmcstat_args *args, struct pmc_plugins *plugins,
struct pmcstat_stats *pmcstat_stats);
@ -344,9 +344,9 @@ void pmcstat_image_link(struct pmcstat_process *_pp,
struct pmcstat_image *_i, uintfptr_t _lpc);
void pmcstat_process_aout_exec(struct pmcstat_process *_pp,
struct pmcstat_image *_image, uintfptr_t _entryaddr);
struct pmcstat_image *_image, uintptr_t _baseaddr);
void pmcstat_process_exec(struct pmcstat_process *_pp,
pmcstat_interned_string _path, uintfptr_t _entryaddr,
pmcstat_interned_string _path, uintptr_t _baseaddr, uintptr_t _dynaddr,
struct pmcstat_args *args, struct pmc_plugins *plugins,
struct pmcstat_stats *pmcstat_stats);
void pmcstat_image_determine_type(struct pmcstat_image *_image, struct pmcstat_args *args);

View file

@ -353,8 +353,8 @@ pmcstat_analyze_log(struct pmcstat_args *args,
ev.pl_u.pl_x.pl_pathname);
assert(image_path != NULL);
pmcstat_process_exec(pp, image_path,
ev.pl_u.pl_x.pl_entryaddr, args,
plugins, pmcstat_stats);
ev.pl_u.pl_x.pl_baseaddr, ev.pl_u.pl_x.pl_dynaddr,
args, plugins, pmcstat_stats);
break;
case PMCLOG_TYPE_PROCEXIT:

View file

@ -61,11 +61,11 @@ __FBSDID("$FreeBSD$");
void
pmcstat_process_aout_exec(struct pmcstat_process *pp,
struct pmcstat_image *image, uintfptr_t entryaddr)
struct pmcstat_image *image, uintptr_t baseaddr)
{
(void) pp;
(void) image;
(void) entryaddr;
(void) baseaddr;
/* TODO Implement a.out handling */
}
@ -75,18 +75,21 @@ pmcstat_process_aout_exec(struct pmcstat_process *pp,
void
pmcstat_process_elf_exec(struct pmcstat_process *pp,
struct pmcstat_image *image, uintfptr_t entryaddr,
struct pmcstat_image *image, uintptr_t baseaddr, uintptr_t dynaddr,
struct pmcstat_args *args, struct pmc_plugins *plugins,
struct pmcstat_stats *pmcstat_stats)
{
uintmax_t libstart;
struct pmcstat_image *rtldimage;
assert(image->pi_type == PMCSTAT_IMAGE_ELF32 ||
image->pi_type == PMCSTAT_IMAGE_ELF64);
/* Create a map entry for the base executable. */
pmcstat_image_link(pp, image, image->pi_vaddr);
/*
* The exact address where the executable gets mapped in will vary for
* PIEs. The dynamic address recorded at process exec time corresponds
* to the address where the executable's file object had been mapped to.
*/
pmcstat_image_link(pp, image, image->pi_vaddr + dynaddr);
/*
* For dynamically linked executables we need to determine
@ -105,16 +108,14 @@ pmcstat_process_elf_exec(struct pmcstat_process *pp,
* [ TEXT DATA BSS HEAP -->*RTLD SHLIBS <--STACK]
* ^ ^
* 0 VM_MAXUSER_ADDRESS
*
* The exact address where the loader gets mapped in
* will vary according to the size of the executable
* and the limits on the size of the process'es data
* segment at the time of exec(). The entry address
* segment at the time of exec(). The base address
* recorded at process exec time corresponds to the
* 'start' address inside the dynamic linker. From
* this we can figure out the address where the
* runtime loader's file object had been mapped to.
* address where the runtime loader's file object had
* been mapped to.
*/
rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath,
0, args, plugins);
@ -135,8 +136,7 @@ pmcstat_process_elf_exec(struct pmcstat_process *pp,
return;
}
libstart = entryaddr - rtldimage->pi_entry;
pmcstat_image_link(pp, rtldimage, libstart);
pmcstat_image_link(pp, rtldimage, baseaddr);
}
}
@ -146,7 +146,7 @@ pmcstat_process_elf_exec(struct pmcstat_process *pp,
void
pmcstat_process_exec(struct pmcstat_process *pp,
pmcstat_interned_string path, uintfptr_t entryaddr,
pmcstat_interned_string path, uintptr_t baseaddr, uintptr_t dynaddr,
struct pmcstat_args *args, struct pmc_plugins *plugins,
struct pmcstat_stats *pmcstat_stats)
{
@ -167,13 +167,13 @@ pmcstat_process_exec(struct pmcstat_process *pp,
case PMCSTAT_IMAGE_ELF32:
case PMCSTAT_IMAGE_ELF64:
pmcstat_stats->ps_exec_elf++;
pmcstat_process_elf_exec(pp, image, entryaddr,
pmcstat_process_elf_exec(pp, image, baseaddr, dynaddr,
args, plugins, pmcstat_stats);
break;
case PMCSTAT_IMAGE_AOUT:
pmcstat_stats->ps_exec_aout++;
pmcstat_process_aout_exec(pp, image, entryaddr);
pmcstat_process_aout_exec(pp, image, baseaddr);
break;
case PMCSTAT_IMAGE_INDETERMINABLE:

View file

@ -198,9 +198,9 @@ CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 5*4 + TSDELTA);
CTASSERT(sizeof(struct pmclog_pmcdetach) == 5*4 + TSDELTA);
CTASSERT(sizeof(struct pmclog_proccsw) == 7*4 + 8 + TSDELTA);
CTASSERT(sizeof(struct pmclog_procexec) == 5*4 + PATH_MAX +
sizeof(uintfptr_t) + TSDELTA);
2*sizeof(uintptr_t) + TSDELTA);
CTASSERT(offsetof(struct pmclog_procexec,pl_pathname) == 5*4 + TSDELTA +
sizeof(uintfptr_t));
2*sizeof(uintptr_t));
CTASSERT(sizeof(struct pmclog_procexit) == 5*4 + 8 + TSDELTA);
CTASSERT(sizeof(struct pmclog_procfork) == 5*4 + TSDELTA);
CTASSERT(sizeof(struct pmclog_sysexit) == 6*4);
@ -1096,7 +1096,7 @@ pmclog_process_proccsw(struct pmc *pm, struct pmc_process *pp, pmc_value_t v, st
void
pmclog_process_procexec(struct pmc_owner *po, pmc_id_t pmid, pid_t pid,
uintfptr_t startaddr, char *path)
uintptr_t baseaddr, uintptr_t dynaddr, char *path)
{
int pathlen, recordlen;
@ -1107,7 +1107,8 @@ pmclog_process_procexec(struct pmc_owner *po, pmc_id_t pmid, pid_t pid,
PMCLOG_RESERVE(po, PMCLOG_TYPE_PROCEXEC, recordlen);
PMCLOG_EMIT32(pid);
PMCLOG_EMIT32(pmid);
PMCLOG_EMITADDR(startaddr);
PMCLOG_EMITADDR(baseaddr);
PMCLOG_EMITADDR(dynaddr);
PMCLOG_EMITSTRING(path,pathlen);
PMCLOG_DESPATCH_SYNC(po);
}

View file

@ -2126,7 +2126,8 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
CK_LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
pmclog_process_procexec(po, PMC_ID_INVALID,
p->p_pid, pk->pm_entryaddr, fullpath);
p->p_pid, pk->pm_baseaddr, pk->pm_dynaddr,
fullpath);
PMC_EPOCH_EXIT();
PROC_LOCK(p);
@ -2170,8 +2171,8 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
if (po->po_sscount == 0 &&
po->po_flags & PMC_PO_OWNS_LOGFILE)
pmclog_process_procexec(po, pm->pm_id,
p->p_pid, pk->pm_entryaddr,
fullpath);
p->p_pid, pk->pm_baseaddr,
pk->pm_dynaddr, fullpath);
}
if (freepath)

View file

@ -919,7 +919,8 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p,
if (PMC_SYSTEM_SAMPLING_ACTIVE() || PMC_PROC_IS_USING_PMCS(p)) {
VOP_UNLOCK(imgp->vp);
pe.pm_credentialschanged = credential_changing;
pe.pm_entryaddr = imgp->entry_addr;
pe.pm_baseaddr = imgp->reloc_base;
pe.pm_dynaddr = imgp->et_dyn_addr;
PMC_CALL_HOOK_X(td, PMC_FN_PROCESS_EXEC, (void *) &pe);
vn_lock(imgp->vp, LK_SHARED | LK_RETRY);

View file

@ -76,7 +76,8 @@ typedef enum ring_type {
struct pmckern_procexec {
int pm_credentialschanged;
uintfptr_t pm_entryaddr;
uintptr_t pm_baseaddr;
uintptr_t pm_dynaddr;
};
struct pmckern_map_in {

View file

@ -202,7 +202,10 @@ struct pmclog_procexec {
PMCLOG_ENTRY_HEADER
uint32_t pl_pid;
uint32_t pl_pmcid;
uintfptr_t pl_start; /* keep 8 byte aligned */
/* keep 8 byte aligned */
uintptr_t pl_base; /* AT_BASE */
/* keep 8 byte aligned */
uintptr_t pl_dyn; /* PIE load base */
char pl_pathname[PATH_MAX];
} __packed;
@ -314,7 +317,7 @@ void pmclog_process_pmcdetach(struct pmc *_pm, pid_t _pid);
void pmclog_process_proccsw(struct pmc *_pm, struct pmc_process *_pp,
pmc_value_t _v, struct thread *);
void pmclog_process_procexec(struct pmc_owner *_po, pmc_id_t _pmid, pid_t _pid,
uintfptr_t _startaddr, char *_path);
uintfptr_t _baseaddr, uintptr_t _dynaddr, char *_path);
void pmclog_process_procexit(struct pmc *_pm, struct pmc_process *_pp);
void pmclog_process_procfork(struct pmc_owner *_po, pid_t _oldpid, pid_t _newpid);
void pmclog_process_sysexit(struct pmc_owner *_po, pid_t _pid);

View file

@ -459,10 +459,11 @@ pmcstat_print_log(void)
ev.pl_u.pl_pc.pl_pcomm);
break;
case PMCLOG_TYPE_PROCEXEC:
PMCSTAT_PRINT_ENTRY("exec","0x%x %d %p \"%s\"",
PMCSTAT_PRINT_ENTRY("exec","0x%x %d %p %p \"%s\"",
ev.pl_u.pl_x.pl_pmcid,
ev.pl_u.pl_x.pl_pid,
(void *) ev.pl_u.pl_x.pl_entryaddr,
(void *)ev.pl_u.pl_x.pl_baseaddr,
(void *)ev.pl_u.pl_x.pl_dynaddr,
ev.pl_u.pl_x.pl_pathname);
break;
case PMCLOG_TYPE_PROCEXIT: