1
0
Files
irix-657m-src/irix/kern/os/proc/pproc_thread.c
2022-09-29 17:59:04 +03:00

390 lines
8.0 KiB
C

/**************************************************************************
* *
* Copyright (C) 1986-1996 Silicon Graphics, Inc. *
* *
* These coded instructions, statements, and computer programs contain *
* unpublished proprietary information of Silicon Graphics, Inc., and *
* are protected by Federal copyright law. They may not be disclosed *
* to third parties or copied or duplicated in any form, in whole or *
* in part, without the prior written consent of Silicon Graphics, Inc. *
* *
**************************************************************************/
#ident "$Revision: 1.12 $"
#include <sys/types.h>
#include <ksys/cred.h>
#include <sys/errno.h>
#include <sys/prctl.h>
#include <sys/proc.h>
#include "pproc_private.h"
#include <sys/sat.h>
#include <sys/systm.h>
#include <sys/kthread.h>
#include <sys/uthread.h>
#include <ksys/vproc.h>
/*
* canblock - check if the running process has permission to block process 'p'
*
* If process is member of share group, and is sharing uids,
* then check permissions against current settings in share group.
* (The process in question may not have had a chance to synchronize).
* The process could however be updating its uid, but that seems
* to be an OK race.
*/
static int
canblock(
proc_t *p,
cred_t *cr)
{
shaddr_t *sa;
uid_t uid;
uid_t suid;
int s;
if (IS_SPROC(&p->p_proxy) && (p->p_proxy.prxy_shmask & PR_SID)) {
/*
* prevent s_cred from changing by another
* share group process doing an set[ug]id
*/
sa = p->p_shaddr;
s = mutex_spinlock(&sa->s_rupdlock);
uid = p->p_shaddr->s_cred->cr_uid;
suid = p->p_shaddr->s_cred->cr_suid;
mutex_spinunlock(&sa->s_rupdlock, s);
} else {
/*
* pcred_access(p) to prevent us from using a stale cred
* pointer if p does a setuid
*/
cred_t *cr = pcred_access(p);
uid = cr->cr_uid;
suid = cr->cr_suid;
pcred_unaccess(p);
}
if (! ( cr->cr_uid == uid || cr->cr_ruid == uid ||
cr->cr_uid == suid || cr->cr_ruid == suid ||
_CAP_CRABLE(cr, CAP_PROC_MGT) )) {
return 0;
} else {
return 1;
}
}
#define BLOCK 0
#define UNBLOCK 1
#define SET 2
/* 3 4 5 are used to refer to the entire share group */
static int
doprocblk(
proc_t *p,
int isself,
cred_t *cr,
int action,
int count)
{
ssleep_t *st;
int s;
uthread_t *ut;
/* share group member might be exiting - ignore */
if (p->p_stat == SZOMB)
return 0;
if (!isself && !canblock(p, cr))
return EPERM;
/* This only operates on single-threaded processes, so
* just grabbing prxy_threads is ok.
*/
ut = prxy_to_thread(&p->p_proxy);
st = &ut->ut_pblock;
s = ut_lock(ut);
if (action == BLOCK) {
/* if not already minimum value, block */
if (st->s_waitcnt < PR_MAXBLOCKCNT) {
blockset(ut, -1, 1, s);
return 0;
}
} else if (action == UNBLOCK) {
/* if not already maximum value, unblock */
if (st->s_slpcnt < PR_MAXBLOCKCNT) {
blockset(ut, 1, 1, s);
return 0;
}
} else { /* (action == SET) */
/* blockset unlocks ut. */
blockset(ut, count, 0, s);
return 0;
}
ut_unlock(ut, s);
return 0;
}
/* pproc layer for the procblk system call */
int
pproc_procblk(
bhv_desc_t *bhv,
int action,
int count,
cred_t *cr, /* Callers credentials */
int isself)
{
proc_t *p;
proc_t *sp;
int blkerr;
int error;
p = BHV_TO_PROC(bhv);
ASSERT(p != NULL);
if (p->p_proxy.prxy_shmask & PR_THREADS)
return(EPERM);
/* Various permission checks */
if (!isself) {
cred_t *pcr = pcred_access(p);
if (_MAC_ACCESS(pcr->cr_mac, cr, MACWRITE)
&& !_CAP_CRABLE(cr, CAP_KILL)) {
pcred_unaccess(p);
return(EPERM);
}
pcred_unaccess(p);
}
if (action > 2 && IS_SPROC(&p->p_proxy)) {
mutex_lock(&p->p_shaddr->s_listlock, 0);
for (sp = p->p_shaddr->s_plink; sp; sp = sp->p_slink) {
if (blkerr = doprocblk(sp, 0, cr, action-3, count))
error = blkerr;
}
mutex_unlock(&p->p_shaddr->s_listlock);
} else
error = doprocblk(p, isself, cr, action, count);
_SAT_PROC_ACCESS(SAT_PROC_ATTR_WRITE, p->p_pid, p->p_cred, error, -1);
return(error);
}
/* Implement process-specific portions of the prctl system call */
int
pproc_prctl(
bhv_desc_t *bhv,
int option,
sysarg_t arg,
int isself,
cred_t *cr, /* Callers credentials */
pid_t callers_pid,
rval_t *rvp)
{
proc_t *p;
uthread_t *ut;
ssleep_t *st;
shaddr_t *sa;
proc_t *lp;
int s;
p = BHV_TO_PROC(bhv);
ASSERT(p != NULL);
switch (option) {
default:
ASSERT(0);
return(EINVAL);
case PR_ISBLOCKED:
/*
* return true (1) if named process is blocked,
* false (0) if process is not blocked,
* error (-1) if process does not exist
* we protect the contents of ut_block with ut_lock.
*/
if (!isself && !canblock(p, cr))
return(EPERM);
rvp->r_val1 = 0;
/* procblock operations are not defined on multi-threaded
* processes, so just return 0 if this is a multi-threaded
* process.
*/
if (p->p_proxy.prxy_shmask & PR_THREADS)
return(0);
/* Not a multi-threaded app, so this is ok. */
ut = prxy_to_thread(&p->p_proxy);
s = ut_lock(ut);
st = &ut->ut_pblock;
if (st->s_waitcnt > st->s_slpcnt || sv_waitq(&st->s_wait))
rvp->r_val1 = 1;
ut_unlock(ut, s);
return(0);
case PR_UNBLKONEXEC:
ASSERT(!isself);
if (!canblock(p, cr))
return(EPERM);
return(0);
case PR_GETSHMASK:
ASSERT(!isself);
if (!IS_SPROC(&p->p_proxy))
return(EINVAL);
if (!canblock(p, cr))
return(EPERM);
/* The semantics of this call require that the caller
* be a member of the same share group as the target
* process. Walk our shaddr list to determine that.
*/
sa = p->p_shaddr;
mutex_lock(&sa->s_listlock, 0);
for (lp = sa->s_plink; lp; lp = lp->p_slink) {
if (lp->p_pid == callers_pid)
break;
}
mutex_unlock(&sa->s_listlock);
if (lp == NULL)
return(EINVAL);
/* Ok, return the shmask of the target process. */
rvp->r_val1 = p->p_proxy.prxy_shmask;
return(0);
case PR_COREPID:
if (!isself && !canblock(p, cr))
return(EPERM);
if (arg == 0)
p_flagclr(p, SCOREPID);
else
p_flagset(p, SCOREPID);
return(0);
case PR_SETEXITSIG:
if (!IS_SPROC(&p->p_proxy)) {
p_flagclr(p, SABORTSIG);
p->p_exitsig = arg;
return(0);
}
/*
* Propagate the exit signal to all members of the share
* group. This turns off sig-on-abort.
*/
sa = p->p_shaddr;
mutex_lock(&sa->s_listlock, 0);
for (p = sa->s_plink; p; p = p->p_slink) {
p->p_exitsig = arg;
p_flagclr(p, SABORTSIG);
}
mutex_unlock(&sa->s_listlock);
return(0);
case PR_SETABORTSIG:
if (!IS_SPROC(&p->p_proxy)) {
p->p_exitsig = arg;
if (arg)
p_flagset(p, SABORTSIG);
return(0);
}
/*
* Propagate the exit signal to all members of the share
* group. This turns off sig-on-exit.
*/
sa = p->p_shaddr;
mutex_lock(&sa->s_listlock, 0);
for (p = sa->s_plink; p; p = p->p_slink) {
p->p_exitsig = arg;
if (arg)
p_flagset(p, SABORTSIG);
}
mutex_unlock(&sa->s_listlock);
return(0);
case PR_TERMCHILD:
p_flagset(p, SCEXIT);
return(0);
case PR_GETNSHARE:
if (!IS_SPROC(&p->p_proxy))
return(0);
rvp->r_val1 = p->p_shaddr->s_refcnt;
return(0);
case PR_LASTSHEXIT:
if (!IS_SPROC(&p->p_proxy)) {
rvp->r_val1 = 1;
} else {
sa = p->p_shaddr;
mutex_lock(&sa->s_listlock, 0);
p_flagset(p, SWILLEXIT);
rvp->r_val1 = 1;
for (p = sa->s_plink; p; p = p->p_slink) {
if ((p->p_flag & SWILLEXIT) == 0) {
rvp->r_val1 = 0;
break;
}
}
mutex_unlock(&sa->s_listlock);
}
return(0);
}
}
int
pproc_set_unblkonexecpid(
bhv_desc_t *bhv,
pid_t unblkpid)
{
proc_t *p;
p = BHV_TO_PROC(bhv);
ASSERT(p != NULL);
/* if process already has an unblock pid then error */
if (p->p_unblkonexecpid)
return(EINVAL);
p->p_unblkonexecpid = unblkpid;
return(0);
}
void
pproc_unblkpid(
bhv_desc_t *bhv)
{
proc_t *p;
uthread_t *ut;
int s;
p = BHV_TO_PROC(bhv);
ASSERT(p != NULL);
if (p->p_proxy.prxy_shmask & PR_THREADS)
return;
ut = prxy_to_thread(&p->p_proxy);
s = ut_lock(ut);
blockset(ut, 1, 1, s); /* blockset unlocks ut. */
}