Print this page
manifest

*** 24,33 **** --- 24,34 ---- * Use is subject to license terms. */ /* * Copyright 2015, Joyent, Inc. + * Copyright 2023 Oxide Computer Company */ #include <sys/types.h> #include <sys/uio.h> #include <sys/param.h>
*** 58,74 **** #include <sys/schedctl.h> #include <vm/as.h> #include <vm/seg.h> #include <fs/proc/prdata.h> #include <sys/contract/process_impl.h> static void pr_settrace(proc_t *, sigset_t *); static int pr_setfpregs(prnode_t *, prfpregset_t *); - #if defined(__sparc) static int pr_setxregs(prnode_t *, prxregset_t *); - static int pr_setasrs(prnode_t *, asrset_t); - #endif static int pr_setvaddr(prnode_t *, caddr_t); static int pr_clearsig(prnode_t *); static int pr_clearflt(prnode_t *); static int pr_watch(prnode_t *, prwatch_t *, int *); static int pr_agent(prnode_t *, prgregset_t, int *); --- 59,73 ---- #include <sys/schedctl.h> #include <vm/as.h> #include <vm/seg.h> #include <fs/proc/prdata.h> #include <sys/contract/process_impl.h> + #include <sys/stdalign.h> static void pr_settrace(proc_t *, sigset_t *); static int pr_setfpregs(prnode_t *, prfpregset_t *); static int pr_setxregs(prnode_t *, prxregset_t *); static int pr_setvaddr(prnode_t *, caddr_t); static int pr_clearsig(prnode_t *); static int pr_clearflt(prnode_t *); static int pr_watch(prnode_t *, prwatch_t *, int *); static int pr_agent(prnode_t *, prgregset_t, int *);
*** 77,86 **** --- 76,92 ---- static int pr_spriv(proc_t *, prpriv_t *, cred_t *); static int pr_szoneid(proc_t *, zoneid_t, cred_t *); static void pauselwps(proc_t *); static void unpauselwps(proc_t *); + /* + * This union represents the size of commands that are generally fixed size in + * /proc. There are some commands that are variable size because the actual data + * is structured. Of things in the latter category, some of these are the same + * across all architectures (e.g. prcred_t, prpriv_t) and some vary and are + * opaque (e.g. the prxregset_t). + */ typedef union { long sig; /* PCKILL, PCUNKILL */ long nice; /* PCNICE */ long timeo; /* PCTWSTOP */ ulong_t flags; /* PCRUN, PCSET, PCUNSET */
*** 89,301 **** sigset_t sigset; /* PCSTRACE, PCSHOLD */ fltset_t fltset; /* PCSFAULT */ sysset_t sysset; /* PCSENTRY, PCSEXIT */ prgregset_t prgregset; /* PCSREG, PCAGENT */ prfpregset_t prfpregset; /* PCSFPREG */ - #if defined(__sparc) - prxregset_t prxregset; /* PCSXREG */ - asrset_t asrset; /* PCSASRS */ - #endif prwatch_t prwatch; /* PCWATCH */ priovec_t priovec; /* PCREAD, PCWRITE */ prcred_t prcred; /* PCSCRED */ prpriv_t prpriv; /* PCSPRIV */ long przoneid; /* PCSZONE */ } arg_t; ! static int pr_control(long, arg_t *, prnode_t *, cred_t *); ! static size_t ! ctlsize(long cmd, size_t resid, arg_t *argp) { ! size_t size = sizeof (long); ! size_t rnd; ! int ngrp; ! switch (cmd) { ! case PCNULL: ! case PCSTOP: ! case PCDSTOP: ! case PCWSTOP: ! case PCCSIG: ! case PCCFAULT: ! break; ! case PCSSIG: ! size += sizeof (siginfo_t); ! break; ! case PCTWSTOP: ! size += sizeof (long); ! break; ! case PCKILL: ! case PCUNKILL: ! case PCNICE: ! size += sizeof (long); ! break; ! case PCRUN: ! case PCSET: ! case PCUNSET: ! size += sizeof (ulong_t); ! break; ! case PCSVADDR: ! size += sizeof (caddr_t); ! break; ! case PCSTRACE: ! case PCSHOLD: ! size += sizeof (sigset_t); ! break; ! case PCSFAULT: ! size += sizeof (fltset_t); ! break; ! case PCSENTRY: ! case PCSEXIT: ! size += sizeof (sysset_t); ! break; ! case PCSREG: ! case PCAGENT: ! size += sizeof (prgregset_t); ! break; ! case PCSFPREG: ! size += sizeof (prfpregset_t); ! break; ! #if defined(__sparc) ! case PCSXREG: ! size += sizeof (prxregset_t); ! break; ! case PCSASRS: ! size += sizeof (asrset_t); ! break; #endif ! case PCWATCH: ! size += sizeof (prwatch_t); ! break; ! case PCREAD: ! case PCWRITE: ! size += sizeof (priovec_t); ! break; ! case PCSCRED: ! size += sizeof (prcred_t); ! break; ! case PCSCREDX: /* ! * We cannot derefence the pr_ngroups fields if it ! * we don't have enough data. */ ! if (resid < size + sizeof (prcred_t) - sizeof (gid_t)) return (0); ! ngrp = argp->prcred.pr_ngroups; ! if (ngrp < 0 || ngrp > ngroups_max) ! return (0); ! /* The result can be smaller than sizeof (prcred_t) */ ! size += sizeof (prcred_t) - sizeof (gid_t); ! size += ngrp * sizeof (gid_t); ! break; ! case PCSPRIV: ! if (resid >= size + sizeof (prpriv_t)) ! size += priv_prgetprivsize(&argp->prpriv); ! else ! return (0); ! break; ! case PCSZONE: ! size += sizeof (long); ! break; ! default: ! return (0); } ! /* Round up to a multiple of long, unless exact amount written */ ! if (size < resid) { ! rnd = size & (sizeof (long) - 1); ! if (rnd != 0) ! size += sizeof (long) - rnd; } ! if (size > resid) return (0); - return (size); } /* * Control operations (lots). */ int ! prwritectl(vnode_t *vp, uio_t *uiop, cred_t *cr) { ! #define MY_BUFFER_SIZE \ ! 100 > 1 + sizeof (arg_t) / sizeof (long) ? \ ! 100 : 1 + sizeof (arg_t) / sizeof (long) ! long buf[MY_BUFFER_SIZE]; ! long *bufp; ! size_t resid = 0; ! size_t size; ! prnode_t *pnp = VTOP(vp); ! int error; ! int locked = 0; ! while (uiop->uio_resid) { /* ! * Read several commands in one gulp. */ ! bufp = buf; ! if (resid) { /* move incomplete command to front of buffer */ ! long *tail; ! if (resid >= sizeof (buf)) ! break; ! tail = (long *)((char *)buf + sizeof (buf) - resid); ! do { ! *bufp++ = *tail++; ! } while ((resid -= sizeof (long)) != 0); } - resid = sizeof (buf) - ((char *)bufp - (char *)buf); - if (resid > uiop->uio_resid) - resid = uiop->uio_resid; - if (error = uiomove((caddr_t)bufp, resid, UIO_WRITE, uiop)) - return (error); - resid += (char *)bufp - (char *)buf; - bufp = buf; ! do { /* loop over commands in buffer */ ! long cmd = bufp[0]; ! arg_t *argp = (arg_t *)&bufp[1]; ! size = ctlsize(cmd, resid, argp); ! if (size == 0) /* incomplete or invalid command */ ! break; /* ! * Perform the specified control operation. */ ! if (!locked) { ! if ((error = prlock(pnp, ZNO)) != 0) ! return (error); ! locked = 1; } ! if (error = pr_control(cmd, argp, pnp, cr)) { ! if (error == -1) /* -1 is timeout */ ! locked = 0; ! else ! return (error); } - bufp = (long *)((char *)bufp + size); - } while ((resid -= size) != 0); ! if (locked) { ! prunlock(pnp); ! locked = 0; } } ! return (resid? EINVAL : 0); } static int ! pr_control(long cmd, arg_t *argp, prnode_t *pnp, cred_t *cr) { prcommon_t *pcp; proc_t *p; int unlocked; int error = 0; if (cmd == PCNULL) return (0); pcp = pnp->pr_common; --- 95,577 ---- sigset_t sigset; /* PCSTRACE, PCSHOLD */ fltset_t fltset; /* PCSFAULT */ sysset_t sysset; /* PCSENTRY, PCSEXIT */ prgregset_t prgregset; /* PCSREG, PCAGENT */ prfpregset_t prfpregset; /* PCSFPREG */ prwatch_t prwatch; /* PCWATCH */ priovec_t priovec; /* PCREAD, PCWRITE */ prcred_t prcred; /* PCSCRED */ prpriv_t prpriv; /* PCSPRIV */ long przoneid; /* PCSZONE */ } arg_t; ! static boolean_t ! prwritectl_pcscredx_sizef(const void *datap, size_t *sizep) ! { ! const prcred_t *cred = datap; ! if (cred->pr_ngroups < 0 || cred->pr_ngroups > ngroups_max) { ! return (B_FALSE); ! } ! ! if (cred->pr_ngroups == 0) { ! *sizep = 0; ! } else { ! *sizep = (cred->pr_ngroups - 1) * sizeof (gid_t); ! } ! return (B_TRUE); ! } ! ! static boolean_t ! prwritectl_pcspriv_sizef(const void *datap, size_t *sizep) { ! const prpriv_t *priv = datap; ! *sizep = priv_prgetprivsize(priv) - sizeof (prpriv_t); ! return (B_TRUE); ! } ! /* ! * This structure represents a single /proc write command that we support and ! * metadata about how to ensure we have sufficient data for it. To determine the ! * data that we need to read, this combines information from three different ! * sources for a given named command in 'pcs_cmd'. The main goal is to first ! * make sure we have the right minimum amount of information so we can read and ! * validate the data around variable length structures. ! * ! * o Most commands have a fixed static size. This is represented in the ! * pcs_size member. This also is used to represent the base structure size ! * in the case of entries like PCSCREDX. ! * ! * o Other commands have an unknown minimum size to determine how much data ! * there is and they use the pcs_minf() function to determine the right ! * value. This is often unknown at compile time because it is say a ! * machdep or ISA based feature (ala PCSXREGS) and we'd rather not #ifdef ! * this code to death. This may be skipped and is for most things. The value ! * it returns is added to the static value. ! * ! * o The final piece is the pcs_sizef() function pointer which determines the ! * total required size for this. It is given a pointer that has at least ! * pcs_size and pcs_minf() bytes. This is used to determine the total ! * expected size of the structure. Callers must not dereference data beyond ! * what they've indicated previously. This should only return exra bytes ! * that are required beyond what was already indicated between the two ! * functions. ! * ! * In all cases, the core prwritectl() logic will determine if there is ! * sufficient step along the way for each of these to proceed. ! */ ! typedef struct proc_control_info { ! long pcs_cmd; ! size_t pcs_size; ! boolean_t (*pcs_minf)(size_t *); ! boolean_t (*pcs_sizef)(const void *, size_t *); ! } proc_control_info_t; ! ! static const proc_control_info_t proc_ctl_info[] = { ! { PCNULL, 0, NULL, NULL }, ! { PCSTOP, 0, NULL, NULL }, ! { PCDSTOP, 0, NULL, NULL }, ! { PCWSTOP, 0, NULL, NULL }, ! { PCCSIG, 0, NULL, NULL }, ! { PCCFAULT, 0, NULL, NULL }, ! { PCSSIG, sizeof (siginfo_t), NULL, NULL }, ! { PCTWSTOP, sizeof (long), NULL, NULL }, ! { PCKILL, sizeof (long), NULL, NULL }, ! { PCUNKILL, sizeof (long), NULL, NULL }, ! { PCNICE, sizeof (long), NULL, NULL }, ! { PCRUN, sizeof (ulong_t), NULL, NULL }, ! { PCSET, sizeof (ulong_t), NULL, NULL }, ! { PCUNSET, sizeof (ulong_t), NULL, NULL }, ! { PCSTRACE, sizeof (sigset_t), NULL, NULL }, ! { PCSHOLD, sizeof (sigset_t), NULL, NULL }, ! { PCSFAULT, sizeof (fltset_t), NULL, NULL }, ! { PCSENTRY, sizeof (sysset_t), NULL, NULL }, ! { PCSEXIT, sizeof (sysset_t), NULL, NULL }, ! { PCSREG, sizeof (prgregset_t), NULL, NULL }, ! { PCAGENT, sizeof (prgregset_t), NULL, NULL }, ! { PCSFPREG, sizeof (prfpregset_t), NULL, NULL }, ! { PCSXREG, 0, prwriteminxreg, prwritesizexreg }, ! { PCWATCH, sizeof (prwatch_t), NULL }, ! { PCREAD, sizeof (priovec_t), NULL, NULL }, ! { PCWRITE, sizeof (priovec_t), NULL, NULL }, ! { PCSCRED, sizeof (prcred_t), NULL, NULL }, ! { PCSCREDX, sizeof (prcred_t), NULL, prwritectl_pcscredx_sizef }, ! { PCSPRIV, sizeof (prpriv_t), NULL, prwritectl_pcspriv_sizef }, ! { PCSZONE, sizeof (long), NULL }, ! }; ! ! /* ! * We need a default buffer that we're going to allocate when we need memory to ! * read control operations. This is on average large enough to hold multiple ! * control operations. We leave this as a smaller value on debug builds just ! * to exercise our reallocation logic. ! */ ! #ifdef DEBUG ! #define PROC_CTL_DEFSIZE 32 ! #else ! #define PROC_CTL_DEFSIZE 1024 #endif ! ! /* ! * This structure is used to track all of the information that we have around a ! * prwritectl call. This is used to reduce function parameters and make state ! * clear. ! */ ! typedef struct { ! void *prwc_buf; ! size_t prwc_buflen; ! size_t prwc_curvalid; ! uio_t *prwc_uiop; ! prnode_t *prwc_pnp; ! boolean_t prwc_locked; ! boolean_t prwc_need32; ! void *prwc_buf32; ! } prwritectl_t; ! ! /* ! * Attempt to read in at least needed data. If we need to read in data, then we ! * will try to fill in as much data as required. ! */ ! static int ! prwritectl_readin(prwritectl_t *prwc, size_t needed) ! { ! int ret; ! size_t toread; ! void *start; ! /* ! * If we have as much data as we need then we're good to go. */ ! if (prwc->prwc_curvalid > needed) { ! ASSERT3U(prwc->prwc_buflen, >=, prwc->prwc_curvalid); ! ASSERT3U(prwc->prwc_buflen, >=, needed); return (0); ! } ! /* ! * We don't have all of our data. We must make sure of several things: ! * ! * 1. That there actually is enough data in the uio_t for what we ! * need, considering what we've already read. ! * 2. If the process is locked, at this point, we want to unlock it ! * before we deal with any I/O or memory allocation. Otherwise we ! * can wreak havoc with p_lock / paging. ! * 3. We need to make sure that our buffer is large enough to actually ! * fit it all. ! * 4. Only at that point can we actually perform the read. ! */ ! if (needed - prwc->prwc_curvalid > prwc->prwc_uiop->uio_resid) { ! return (EINVAL); } ! if (prwc->prwc_locked) { ! prunlock(prwc->prwc_pnp); ! prwc->prwc_locked = B_FALSE; ! } ! if (needed > prwc->prwc_buflen) { ! size_t new_len = P2ROUNDUP(needed, PROC_CTL_DEFSIZE); ! prwc->prwc_buf = kmem_rezalloc(prwc->prwc_buf, ! prwc->prwc_buflen, new_len, KM_SLEEP); ! if (prwc->prwc_need32) { ! prwc->prwc_buf32 = kmem_rezalloc(prwc->prwc_buf32, ! prwc->prwc_buflen, new_len, KM_SLEEP); } + prwc->prwc_buflen = new_len; + } ! toread = MIN(prwc->prwc_buflen - prwc->prwc_curvalid, ! prwc->prwc_uiop->uio_resid); ! ASSERT3U(toread, >=, needed - prwc->prwc_curvalid); ! start = (void *)((uintptr_t)prwc->prwc_buf + prwc->prwc_curvalid); ! if ((ret = uiomove(start, toread, UIO_WRITE, prwc->prwc_uiop)) != 0) { ! return (ret); ! } ! ! prwc->prwc_curvalid += toread; return (0); } + static const proc_control_info_t * + prwritectl_cmd_identify(const prwritectl_t *prwc, + const proc_control_info_t *info, size_t ninfo, size_t cmdsize) + { + long cmd; + + ASSERT(cmdsize == sizeof (int32_t) || cmdsize == sizeof (long)); + if (cmdsize == 4) { + cmd = (long)*(int32_t *)prwc->prwc_buf; + } else { + cmd = *(long *)prwc->prwc_buf; + } + + + for (size_t i = 0; i < ninfo; i++) { + if (info[i].pcs_cmd == cmd) { + return (&info[i]); + } + } + + return (NULL); + } + /* * Control operations (lots). + * + * Users can submit one or more commands to us in the uio_t. They are required + * to always be complete messages. The first one that fails will cause all + * subsequent things to fail. Processing this can be a little tricky as the + * actual data size that may be required is variable, not all structures are + * fixed sizes and some vary based on the instructing set (e.g. x86 vs. + * something else). + * + * The way that we handle process locking deserves some consideration. Prior to + * the colonization of prwritectl and the support for dynamic sizing of data, + * the logic would try to read in a large chunk of data and keep a process + * locked throughout that period and then unlock it before reading more data. As + * such, we mimic that logically and basically lock it before executing the + * first (or any subsequent) command and then only unlock it either when we're + * done entirely or we need to allocate memory or read from the process. + * + * This function is a common implementation for both the ILP32 and LP64 entry + * points as they are mostly the same except for the sizing and control function + * we call. */ int ! prwritectl_common(vnode_t *vp, uio_t *uiop, cred_t *cr, ! const proc_control_info_t *proc_info, size_t ninfo, size_t cmdsize, ! int (*pr_controlf)(long, void *, prnode_t *, cred_t *)) { ! int ret; ! prwritectl_t prwc; ! VERIFY(cmdsize == sizeof (int32_t) || cmdsize == sizeof (long)); ! ! bzero(&prwc, sizeof (prwc)); ! prwc.prwc_pnp = VTOP(vp); ! prwc.prwc_uiop = uiop; ! prwc.prwc_need32 = cmdsize == sizeof (int32_t); ! /* ! * We may have multiple commands to read and want to try to minimize the ! * amount of reading that we do. Our callers expect us to have a ! * contiguous buffer for a command's actual implementation. However, we ! * must have at least a single long worth of data, otherwise it's not ! * worth continuing. */ ! while (uiop->uio_resid > 0 || prwc.prwc_curvalid > 0) { ! const proc_control_info_t *proc_cmd; ! void *data; ! /* ! * Check if we have enough data to identify a command. If not, ! * we read as much as we can in one gulp. ! */ ! if ((ret = prwritectl_readin(&prwc, cmdsize)) != 0) { ! goto out; } ! /* ! * Identify the command and figure out how how much data we ! * should have read in the kernel. Some commands have a variable ! * length and we need to make sure the minimum is met before ! * asking how much there is in general. Most things know what ! * the minimum length is and this pcs_minf() is not implemented. ! * However things that are ISA-specific require us to ask that ! * first. ! * ! * We also must be aware that there may not actually be enough ! * data present in the uio_t. ! */ ! if ((proc_cmd = prwritectl_cmd_identify(&prwc, proc_info, ! ninfo, cmdsize)) == NULL) { ! ret = EINVAL; ! goto out; ! } ! size_t needed_data = cmdsize + proc_cmd->pcs_size; ! if (proc_cmd->pcs_minf != NULL) { ! size_t min; ! ! if (!proc_cmd->pcs_minf(&min)) { ! ret = EINVAL; ! goto out; ! } ! ! needed_data += min; ! } ! ! if (proc_cmd->pcs_sizef != NULL) { ! size_t extra; ! /* ! * Make sure we have the minimum amount of data that ! * they asked us to between the static and minf ! * function. */ ! if ((ret = prwritectl_readin(&prwc, needed_data)) != ! 0) { ! goto out; } ! ! VERIFY3U(prwc.prwc_curvalid, >, cmdsize); ! data = (void *)((uintptr_t)prwc.prwc_buf + cmdsize); ! if (!proc_cmd->pcs_sizef(data, &extra)) { ! ret = EINVAL; ! goto out; } ! needed_data += extra; } + + /* + * Now that we know how much data we're supposed to have, + * finally ensure we have the total amount we need. + */ + if ((ret = prwritectl_readin(&prwc, needed_data)) != 0) { + goto out; } ! ! /* ! * /proc has traditionally assumed control writes come in ! * multiples of a long. This is 4 bytes for ILP32 and 8 bytes ! * for LP64. When calculating the required size for a structure, ! * it would always round that up to the next long. However, the ! * exact combination of circumstances changes with the ! * introduction of the 64-bit kernel. For 64-bit processes we ! * round up when the current command we're processing isn't the ! * last one. ! * ! * Because of our tracking structures and caching we need to ! * look beyond the uio_t to make this determination. In ! * particular, the uio_t can have a zero resid, but we may still ! * have additional data to read as indicated by prwc_curvalid ! * exceeded the current command size. In the end, we must check ! * both of these cases. ! */ ! if ((needed_data % cmdsize) != 0) { ! if (cmdsize == sizeof (int32_t) || ! prwc.prwc_curvalid > needed_data || ! prwc.prwc_uiop->uio_resid > 0) { ! needed_data = P2ROUNDUP(needed_data, ! cmdsize); ! if ((ret = prwritectl_readin(&prwc, ! needed_data)) != 0) { ! goto out; ! } ! } ! } ! ! if (!prwc.prwc_locked) { ! ret = prlock(prwc.prwc_pnp, ZNO); ! if (ret != 0) { ! goto out; ! } ! prwc.prwc_locked = B_TRUE; ! } ! ! /* ! * Run our actual command. When there is an error, then the ! * underlying pr_control call will have unlocked the prnode_t ! * on our behalf. pr_control can return -1, which is a special ! * error indicating a timeout occurred. In such a case the node ! * is unlocked; however, that we are supposed to continue ! * processing commands regardless. ! * ! * Finally, we must deal with with one actual wrinkle. The LP64 ! * based logic always guarantees that we have data that is ! * 8-byte aligned. However, the ILP32 logic is 4-byte aligned ! * and the rest of the /proc code assumes it can always ! * dereference it. If we're not aligned, we have to bcopy it to ! * a temporary buffer. ! */ ! data = (void *)((uintptr_t)prwc.prwc_buf + cmdsize); ! #ifdef DEBUG ! if (cmdsize == sizeof (long)) { ! VERIFY0((uintptr_t)data % alignof (long)); ! } ! #endif ! if (prwc.prwc_need32 && ((uintptr_t)data % alignof (long)) != ! 0 && needed_data > cmdsize) { ! bcopy(data, prwc.prwc_buf32, needed_data - cmdsize); ! data = prwc.prwc_buf32; ! } ! ret = pr_controlf(proc_cmd->pcs_cmd, data, prwc.prwc_pnp, cr); ! if (ret != 0) { ! prwc.prwc_locked = B_FALSE; ! if (ret > 0) { ! goto out; ! } ! } ! ! /* ! * Finally, now that we have processed this command, we need to ! * move on. To make our life simple, we basically shift all the ! * data in our buffer over to indicate it's been consumed. While ! * a little wasteful, this simplifies buffer management and ! * guarantees that command processing uses a semi-sanitized ! * state. Visually, this is the following transformation: ! * ! * 0 20 prwc.prwc_curvalid ! * +------------------+----------------+ ! * | needed_data | remaining_data | ! * +------------------+----------------+ ! * ! * In the above example we are shifting all the data over by 20, ! * so remaining data starts at 0. This leaves us needed_data ! * bytes to clean up from what was valid. ! */ ! if (prwc.prwc_buf32 != NULL) { ! bzero(prwc.prwc_buf32, needed_data - cmdsize); ! } ! ! if (prwc.prwc_curvalid > needed_data) { ! size_t save_size = prwc.prwc_curvalid - needed_data; ! void *first_save = (void *)((uintptr_t)prwc.prwc_buf + ! needed_data); ! memmove(prwc.prwc_buf, first_save, save_size); ! void *first_zero = (void *)((uintptr_t)prwc.prwc_buf + ! save_size); ! bzero(first_zero, needed_data); ! } else { ! bzero(prwc.prwc_buf, prwc.prwc_curvalid); ! } ! prwc.prwc_curvalid -= needed_data; ! } ! ! /* ! * We've managed to successfully process everything. We can actually say ! * this was successful now. ! */ ! ret = 0; ! ! out: ! if (prwc.prwc_locked) { ! prunlock(prwc.prwc_pnp); ! prwc.prwc_locked = B_FALSE; ! } ! ! if (prwc.prwc_buf != NULL) { ! kmem_free(prwc.prwc_buf, prwc.prwc_buflen); ! } ! ! if (prwc.prwc_buf32 != NULL) { ! VERIFY(prwc.prwc_need32); ! kmem_free(prwc.prwc_buf32, prwc.prwc_buflen); ! } ! ! return (ret); } static int ! pr_control(long cmd, void *generic, prnode_t *pnp, cred_t *cr) { prcommon_t *pcp; proc_t *p; int unlocked; int error = 0; + arg_t *argp = generic; if (cmd == PCNULL) return (0); pcp = pnp->pr_common;
*** 420,442 **** case PCSFPREG: /* set floating-point registers */ error = pr_setfpregs(pnp, &argp->prfpregset); break; case PCSXREG: /* set extra registers */ ! #if defined(__sparc) ! error = pr_setxregs(pnp, &argp->prxregset); ! #else ! error = EINVAL; ! #endif break; - #if defined(__sparc) - case PCSASRS: /* set ancillary state registers */ - error = pr_setasrs(pnp, argp->asrset); - break; - #endif - case PCSVADDR: /* set virtual address at which to resume */ error = pr_setvaddr(pnp, argp->vaddr); break; case PCSHOLD: /* set signal-hold mask */ --- 696,708 ---- case PCSFPREG: /* set floating-point registers */ error = pr_setfpregs(pnp, &argp->prfpregset); break; case PCSXREG: /* set extra registers */ ! error = pr_setxregs(pnp, (prxregset_t *)argp); break; case PCSVADDR: /* set virtual address at which to resume */ error = pr_setvaddr(pnp, argp->vaddr); break; case PCSHOLD: /* set signal-hold mask */
*** 491,500 **** --- 757,773 ---- if (error) prunlock(pnp); return (error); } + int + prwritectl(vnode_t *vp, uio_t *uiop, cred_t *cr) + { + return (prwritectl_common(vp, uiop, cr, proc_ctl_info, + ARRAY_SIZE(proc_ctl_info), sizeof (long), pr_control)); + } + #ifdef _SYSCALL32_IMPL typedef union { int32_t sig; /* PCKILL, PCUNKILL */ int32_t nice; /* PCNICE */
*** 505,731 **** sigset_t sigset; /* PCSTRACE, PCSHOLD */ fltset_t fltset; /* PCSFAULT */ sysset_t sysset; /* PCSENTRY, PCSEXIT */ prgregset32_t prgregset; /* PCSREG, PCAGENT */ prfpregset32_t prfpregset; /* PCSFPREG */ - #if defined(__sparc) - prxregset_t prxregset; /* PCSXREG */ - #endif prwatch32_t prwatch; /* PCWATCH */ priovec32_t priovec; /* PCREAD, PCWRITE */ prcred32_t prcred; /* PCSCRED */ prpriv_t prpriv; /* PCSPRIV */ int32_t przoneid; /* PCSZONE */ } arg32_t; - static int pr_control32(int32_t, arg32_t *, prnode_t *, cred_t *); static int pr_setfpregs32(prnode_t *, prfpregset32_t *); ! /* ! * Note that while ctlsize32() can use argp, it must do so only in a way ! * that assumes 32-bit rather than 64-bit alignment as argp is a pointer ! * to an array of 32-bit values and only 32-bit alignment is ensured. ! */ ! static size_t ! ctlsize32(int32_t cmd, size_t resid, arg32_t *argp) { ! size_t size = sizeof (int32_t); ! size_t rnd; ! int ngrp; ! switch (cmd) { ! case PCNULL: ! case PCSTOP: ! case PCDSTOP: ! case PCWSTOP: ! case PCCSIG: ! case PCCFAULT: ! break; ! case PCSSIG: ! size += sizeof (siginfo32_t); ! break; ! case PCTWSTOP: ! size += sizeof (int32_t); ! break; ! case PCKILL: ! case PCUNKILL: ! case PCNICE: ! size += sizeof (int32_t); ! break; ! case PCRUN: ! case PCSET: ! case PCUNSET: ! size += sizeof (uint32_t); ! break; ! case PCSVADDR: ! size += sizeof (caddr32_t); ! break; ! case PCSTRACE: ! case PCSHOLD: ! size += sizeof (sigset_t); ! break; ! case PCSFAULT: ! size += sizeof (fltset_t); ! break; ! case PCSENTRY: ! case PCSEXIT: ! size += sizeof (sysset_t); ! break; ! case PCSREG: ! case PCAGENT: ! size += sizeof (prgregset32_t); ! break; ! case PCSFPREG: ! size += sizeof (prfpregset32_t); ! break; ! #if defined(__sparc) ! case PCSXREG: ! size += sizeof (prxregset_t); ! break; ! #endif ! case PCWATCH: ! size += sizeof (prwatch32_t); ! break; ! case PCREAD: ! case PCWRITE: ! size += sizeof (priovec32_t); ! break; ! case PCSCRED: ! size += sizeof (prcred32_t); ! break; ! case PCSCREDX: ! /* ! * We cannot derefence the pr_ngroups fields if it ! * we don't have enough data. ! */ ! if (resid < size + sizeof (prcred32_t) - sizeof (gid32_t)) ! return (0); ! ngrp = argp->prcred.pr_ngroups; ! if (ngrp < 0 || ngrp > ngroups_max) ! return (0); ! ! /* The result can be smaller than sizeof (prcred32_t) */ ! size += sizeof (prcred32_t) - sizeof (gid32_t); ! size += ngrp * sizeof (gid32_t); ! break; ! case PCSPRIV: ! if (resid >= size + sizeof (prpriv_t)) ! size += priv_prgetprivsize(&argp->prpriv); ! else ! return (0); ! break; ! case PCSZONE: ! size += sizeof (int32_t); ! break; ! default: ! return (0); } ! /* Round up to a multiple of int32_t */ ! rnd = size & (sizeof (int32_t) - 1); ! ! if (rnd != 0) ! size += sizeof (int32_t) - rnd; ! ! if (size > resid) ! return (0); ! return (size); } /* ! * Control operations (lots). */ ! int ! prwritectl32(struct vnode *vp, struct uio *uiop, cred_t *cr) ! { ! #define MY_BUFFER_SIZE32 \ ! 100 > 1 + sizeof (arg32_t) / sizeof (int32_t) ? \ ! 100 : 1 + sizeof (arg32_t) / sizeof (int32_t) ! int32_t buf[MY_BUFFER_SIZE32]; ! int32_t *bufp; ! arg32_t arg; ! size_t resid = 0; ! size_t size; ! prnode_t *pnp = VTOP(vp); ! int error; ! int locked = 0; - while (uiop->uio_resid) { - /* - * Read several commands in one gulp. - */ - bufp = buf; - if (resid) { /* move incomplete command to front of buffer */ - int32_t *tail; - - if (resid >= sizeof (buf)) - break; - tail = (int32_t *)((char *)buf + sizeof (buf) - resid); - do { - *bufp++ = *tail++; - } while ((resid -= sizeof (int32_t)) != 0); - } - resid = sizeof (buf) - ((char *)bufp - (char *)buf); - if (resid > uiop->uio_resid) - resid = uiop->uio_resid; - if (error = uiomove((caddr_t)bufp, resid, UIO_WRITE, uiop)) - return (error); - resid += (char *)bufp - (char *)buf; - bufp = buf; - - do { /* loop over commands in buffer */ - int32_t cmd = bufp[0]; - arg32_t *argp = (arg32_t *)&bufp[1]; - - size = ctlsize32(cmd, resid, argp); - if (size == 0) /* incomplete or invalid command */ - break; - /* - * Perform the specified control operation. - */ - if (!locked) { - if ((error = prlock(pnp, ZNO)) != 0) - return (error); - locked = 1; - } - - /* - * Since some members of the arg32_t union contain - * 64-bit values (which must be 64-bit aligned), we - * can't simply pass a pointer to the structure as - * it may be unaligned. Note that we do pass the - * potentially unaligned structure to ctlsize32() - * above, but that uses it a way that makes no - * assumptions about alignment. - */ - ASSERT(size - sizeof (cmd) <= sizeof (arg)); - bcopy(argp, &arg, size - sizeof (cmd)); - - if (error = pr_control32(cmd, &arg, pnp, cr)) { - if (error == -1) /* -1 is timeout */ - locked = 0; - else - return (error); - } - bufp = (int32_t *)((char *)bufp + size); - } while ((resid -= size) != 0); - - if (locked) { - prunlock(pnp); - locked = 0; - } - } - return (resid? EINVAL : 0); - } - static int ! pr_control32(int32_t cmd, arg32_t *argp, prnode_t *pnp, cred_t *cr) { prcommon_t *pcp; proc_t *p; int unlocked; int error = 0; if (cmd == PCNULL) return (0); pcp = pnp->pr_common; --- 778,860 ---- sigset_t sigset; /* PCSTRACE, PCSHOLD */ fltset_t fltset; /* PCSFAULT */ sysset_t sysset; /* PCSENTRY, PCSEXIT */ prgregset32_t prgregset; /* PCSREG, PCAGENT */ prfpregset32_t prfpregset; /* PCSFPREG */ prwatch32_t prwatch; /* PCWATCH */ priovec32_t priovec; /* PCREAD, PCWRITE */ prcred32_t prcred; /* PCSCRED */ prpriv_t prpriv; /* PCSPRIV */ int32_t przoneid; /* PCSZONE */ } arg32_t; static int pr_setfpregs32(prnode_t *, prfpregset32_t *); ! static boolean_t ! prwritectl_pcscredx32_sizef(const void *datap, size_t *sizep) { ! const prcred32_t *cred = datap; ! if (cred->pr_ngroups < 0 || cred->pr_ngroups > ngroups_max) { ! return (B_FALSE); } ! if (cred->pr_ngroups == 0) { ! *sizep = 0; ! } else { ! *sizep = (cred->pr_ngroups - 1) * sizeof (gid32_t); ! } ! return (B_TRUE); } /* ! * When dealing with ILP32 code, we are not at a point where we can assume ! * 64-bit aligned data. Any functions that are operating here must be aware of ! * that. */ ! static const proc_control_info_t proc_ctl_info32[] = { ! { PCNULL, 0, NULL, NULL }, ! { PCSTOP, 0, NULL, NULL }, ! { PCDSTOP, 0, NULL, NULL }, ! { PCWSTOP, 0, NULL, NULL }, ! { PCCSIG, 0, NULL, NULL }, ! { PCCFAULT, 0, NULL, NULL }, ! { PCSSIG, sizeof (siginfo32_t), NULL, NULL }, ! { PCTWSTOP, sizeof (int32_t), NULL, NULL }, ! { PCKILL, sizeof (int32_t), NULL, NULL }, ! { PCUNKILL, sizeof (int32_t), NULL, NULL }, ! { PCNICE, sizeof (int32_t), NULL, NULL }, ! { PCRUN, sizeof (uint32_t), NULL, NULL }, ! { PCSET, sizeof (uint32_t), NULL, NULL }, ! { PCUNSET, sizeof (uint32_t), NULL, NULL }, ! { PCSVADDR, sizeof (caddr32_t), NULL, NULL }, ! { PCSTRACE, sizeof (sigset_t), NULL, NULL }, ! { PCSHOLD, sizeof (sigset_t), NULL, NULL }, ! { PCSFAULT, sizeof (fltset_t), NULL, NULL }, ! { PCSENTRY, sizeof (sysset_t), NULL, NULL }, ! { PCSEXIT, sizeof (sysset_t), NULL, NULL }, ! { PCSREG, sizeof (prgregset32_t), NULL, NULL }, ! { PCAGENT, sizeof (prgregset32_t), NULL, NULL }, ! { PCSFPREG, sizeof (prfpregset32_t), NULL, NULL }, ! { PCSXREG, 0, prwriteminxreg, prwritesizexreg }, ! { PCWATCH, sizeof (prwatch32_t), NULL }, ! { PCREAD, sizeof (priovec32_t), NULL, NULL }, ! { PCWRITE, sizeof (priovec32_t), NULL, NULL }, ! { PCSCRED, sizeof (prcred32_t), NULL, NULL }, ! { PCSCREDX, sizeof (prcred32_t), NULL, prwritectl_pcscredx32_sizef }, ! { PCSPRIV, sizeof (prpriv_t), NULL, prwritectl_pcspriv_sizef }, ! { PCSZONE, sizeof (long), NULL }, ! }; static int ! pr_control32(long cmd, void *generic, prnode_t *pnp, cred_t *cr) { prcommon_t *pcp; proc_t *p; int unlocked; int error = 0; + arg32_t *argp = generic; if (cmd == PCNULL) return (0); pcp = pnp->pr_common;
*** 868,885 **** else error = pr_setfpregs32(pnp, &argp->prfpregset); break; case PCSXREG: /* set extra registers */ - #if defined(__sparc) if (PROCESS_NOT_32BIT(p)) error = EOVERFLOW; else ! error = pr_setxregs(pnp, &argp->prxregset); ! #else ! error = EINVAL; ! #endif break; case PCSVADDR: /* set virtual address at which to resume */ if (PROCESS_NOT_32BIT(p)) error = EOVERFLOW; --- 997,1010 ---- else error = pr_setfpregs32(pnp, &argp->prfpregset); break; case PCSXREG: /* set extra registers */ if (PROCESS_NOT_32BIT(p)) error = EOVERFLOW; else ! error = pr_setxregs(pnp, (prxregset_t *)argp); break; case PCSVADDR: /* set virtual address at which to resume */ if (PROCESS_NOT_32BIT(p)) error = EOVERFLOW;
*** 985,994 **** --- 1110,1125 ---- if (error) prunlock(pnp); return (error); } + int + prwritectl32(struct vnode *vp, struct uio *uiop, cred_t *cr) + { + return (prwritectl_common(vp, uiop, cr, proc_ctl_info32, + ARRAY_SIZE(proc_ctl_info32), sizeof (int32_t), pr_control32)); + } #endif /* _SYSCALL32_IMPL */ /* * Return the specific or chosen thread/lwp for a control operation. * Returns with the thread locked via thread_lock(t).
*** 1708,1722 **** return (0); } #endif /* _SYSCALL32_IMPL */ - #if defined(__sparc) /* ARGSUSED */ static int pr_setxregs(prnode_t *pnp, prxregset_t *prxregset) { proc_t *p = pnp->pr_common->prc_proc; kthread_t *t = pr_thread(pnp); /* returns locked thread */ if (!ISTOPPED(t) && !VSTOPPED(t) && !DSTOPPED(t)) { thread_unlock(t); --- 1839,1853 ---- return (0); } #endif /* _SYSCALL32_IMPL */ /* ARGSUSED */ static int pr_setxregs(prnode_t *pnp, prxregset_t *prxregset) { + int error; proc_t *p = pnp->pr_common->prc_proc; kthread_t *t = pr_thread(pnp); /* returns locked thread */ if (!ISTOPPED(t) && !VSTOPPED(t) && !DSTOPPED(t)) { thread_unlock(t);
*** 1727,1764 **** if (!prhasx(p)) return (EINVAL); /* No extra register support */ /* drop p_lock while touching the lwp's stack */ mutex_exit(&p->p_lock); ! prsetprxregs(ttolwp(t), (caddr_t)prxregset); mutex_enter(&p->p_lock); ! return (0); } static int - pr_setasrs(prnode_t *pnp, asrset_t asrset) - { - proc_t *p = pnp->pr_common->prc_proc; - kthread_t *t = pr_thread(pnp); /* returns locked thread */ - - if (!ISTOPPED(t) && !VSTOPPED(t) && !DSTOPPED(t)) { - thread_unlock(t); - return (EBUSY); - } - thread_unlock(t); - - /* drop p_lock while touching the lwp's stack */ - mutex_exit(&p->p_lock); - prsetasregs(ttolwp(t), asrset); - mutex_enter(&p->p_lock); - - return (0); - } - #endif - - static int pr_setvaddr(prnode_t *pnp, caddr_t vaddr) { proc_t *p = pnp->pr_common->prc_proc; kthread_t *t = pr_thread(pnp); /* returns locked thread */ --- 1858,1874 ---- if (!prhasx(p)) return (EINVAL); /* No extra register support */ /* drop p_lock while touching the lwp's stack */ mutex_exit(&p->p_lock); ! error = prsetprxregs(ttolwp(t), prxregset); mutex_enter(&p->p_lock); ! return (error); } static int pr_setvaddr(prnode_t *pnp, caddr_t vaddr) { proc_t *p = pnp->pr_common->prc_proc; kthread_t *t = pr_thread(pnp); /* returns locked thread */