diff --git a/HISTORY.rst b/HISTORY.rst index 49e9ed4c2..fbf5ac004 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -22,6 +22,13 @@ XXXX-XX-XX - 1240_: [Windows] cpu_times() float loses accuracy in a long running system. (patch by stswandering) - 1245_: [Linux] sensors_temperatures() may fail with IOError "no such file". +- 1193_: [SunOS] Return uid/gid from /proc/pid/psinfo if there aren't + enough permissions for /proc/pid/cred +- 1194_: [SunOS] Return nice value from psinfo as getpriority() doesn't + support real-time processes +- 1194_: [SunOS] Fix double free in psutil_proc_cpu_num() +- 1194_: [SunOS] Fix undefined behavior related to strict-aliasing rules + and warnings 5.4.3 ===== diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index 5471d5aa4..35b4b092b 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -79,7 +79,11 @@ nice=4, num_threads=5, status=6, - ttynr=7) + ttynr=7, + uid=8, + euid=9, + gid=10, + egid=11) # ===================================================================== @@ -394,7 +398,10 @@ def _proc_basic_info(self): @memoize_when_activated def _proc_cred(self): - return cext.proc_cred(self.pid, self._procfs_path) + @wrap_exceptions + def proc_cred(self): + return cext.proc_cred(self.pid, self._procfs_path) + return proc_cred(self) @wrap_exceptions def name(self): @@ -432,27 +439,10 @@ def num_threads(self): @wrap_exceptions def nice_get(self): - # Note #1: for some reason getpriority(3) return ESRCH (no such - # process) for certain low-pid processes, no matter what (even - # as root). - # The process actually exists though, as it has a name, - # creation time, etc. - # The best thing we can do here appears to be raising AD. - # Note: tested on Solaris 11; on Open Solaris 5 everything is - # fine. - # - # Note #2: we also can get niceness from /proc/pid/psinfo - # but it's wrong, see: - # https://github.com/giampaolo/psutil/issues/1082 - try: - return cext_posix.getpriority(self.pid) - except EnvironmentError as err: - # 48 is 'operation not supported' but errno does not expose - # it. It occurs for low system pids. - if err.errno in (errno.ENOENT, errno.ESRCH, 48): - if pid_exists(self.pid): - raise AccessDenied(self.pid, self._name) - raise + # Note #1: getpriority(3) doesn't work for realtime processes. + # Psinfo is what ps uses, see: + # https://github.com/giampaolo/psutil/issues/1194 + return self._proc_basic_info()[proc_info_map['nice']] @wrap_exceptions def nice_set(self, value): @@ -471,12 +461,22 @@ def ppid(self): @wrap_exceptions def uids(self): - real, effective, saved, _, _, _ = self._proc_cred() + try: + real, effective, saved, _, _, _ = self._proc_cred() + except AccessDenied: + real = self._proc_basic_info()[proc_info_map['uid']] + effective = self._proc_basic_info()[proc_info_map['euid']] + saved = None return _common.puids(real, effective, saved) @wrap_exceptions def gids(self): - _, _, _, real, effective, saved = self._proc_cred() + try: + _, _, _, real, effective, saved = self._proc_cred() + except AccessDenied: + real = self._proc_basic_info()[proc_info_map['gid']] + effective = self._proc_basic_info()[proc_info_map['egid']] + saved = None return _common.puids(real, effective, saved) @wrap_exceptions diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c index 908dbf14a..e08f011c2 100644 --- a/psutil/_psutil_common.c +++ b/psutil/_psutil_common.c @@ -40,7 +40,7 @@ PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size) { * If msg != "" the exception message will change in accordance. */ PyObject * -NoSuchProcess(char *msg) { +NoSuchProcess(const char *msg) { PyObject *exc; exc = PyObject_CallFunction( PyExc_OSError, "(is)", ESRCH, strlen(msg) ? msg : strerror(ESRCH)); @@ -55,7 +55,7 @@ NoSuchProcess(char *msg) { * If msg != "" the exception message will change in accordance. */ PyObject * -AccessDenied(char *msg) { +AccessDenied(const char *msg) { PyObject *exc; exc = PyObject_CallFunction( PyExc_OSError, "(is)", EACCES, strlen(msg) ? msg : strerror(EACCES)); diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h index 3db3f5ede..e107166a1 100644 --- a/psutil/_psutil_common.h +++ b/psutil/_psutil_common.h @@ -4,6 +4,9 @@ * found in the LICENSE file. */ +#ifndef PSUTIL_PSUTIL_COMMON_H +#define PSUTIL_PSUTIL_COMMON_H + #include extern int PSUTIL_TESTING; @@ -17,9 +20,11 @@ PyObject* PyUnicode_DecodeFSDefault(char *s); PyObject* PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size); #endif -PyObject* AccessDenied(char *msg); -PyObject* NoSuchProcess(char *msg); +PyObject* AccessDenied(const char *msg); +PyObject* NoSuchProcess(const char *msg); PyObject* psutil_set_testing(PyObject *self, PyObject *args); void psutil_debug(const char* format, ...); void psutil_setup(void); + +#endif // PSUTIL_PSUTIL_COMMON_H diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c index 4d38a3423..99069f56a 100644 --- a/psutil/_psutil_sunos.c +++ b/psutil/_psutil_sunos.c @@ -46,6 +46,7 @@ #include #include #include +#include // fabs() #include "_psutil_common.h" #include "_psutil_posix.h" @@ -58,10 +59,10 @@ /* * Read a file content and fills a C structure with it. */ -int +static int psutil_file_to_struct(char *path, void *fstruct, size_t size) { int fd; - size_t nbytes; + ssize_t nbytes; fd = open(path, O_RDONLY); if (fd == -1) { PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); @@ -73,7 +74,7 @@ psutil_file_to_struct(char *path, void *fstruct, size_t size) { PyErr_SetFromErrno(PyExc_OSError); return 0; } - if (nbytes != size) { + if (nbytes != (ssize_t) size) { close(fd); PyErr_SetString( PyExc_RuntimeError, "read() file structure size mismatch"); @@ -102,17 +103,19 @@ psutil_proc_basic_info(PyObject *self, PyObject *args) { if (! psutil_file_to_struct(path, (void *)&info, sizeof(info))) return NULL; return Py_BuildValue( - "ikkdiiik", + "ikkdiiikiiii", info.pr_ppid, // parent pid info.pr_rssize, // rss info.pr_size, // vms PSUTIL_TV2DOUBLE(info.pr_start), // create time - // XXX - niceness is wrong (20 instead of 0), see: - // https://github.com/giampaolo/psutil/issues/1082 info.pr_lwp.pr_nice, // nice info.pr_nlwp, // no. of threads info.pr_lwp.pr_state, // status code - info.pr_ttydev // tty nr + info.pr_ttydev, // tty nr + (int)info.pr_uid, // real user id + (int)info.pr_euid, // effective user id + (int)info.pr_gid, // real group id + (int)info.pr_egid // effective group id ); } @@ -348,13 +351,11 @@ psutil_proc_cpu_num(PyObject *self, PyObject *args) { int pid; char path[1000]; struct prheader header; - struct lwpsinfo *lwp; - char *lpsinfo = NULL; - char *ptr = NULL; + struct lwpsinfo *lwp = NULL; int nent; int size; int proc_num; - size_t nbytes; + ssize_t nbytes; const char *procfs_path; if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path)) @@ -382,14 +383,14 @@ psutil_proc_cpu_num(PyObject *self, PyObject *args) { // malloc nent = header.pr_nent; size = header.pr_entsize * nent; - ptr = lpsinfo = malloc(size); - if (lpsinfo == NULL) { + lwp = malloc(size); + if (lwp == NULL) { PyErr_NoMemory(); goto error; } // read the rest - nbytes = pread(fd, lpsinfo, size, sizeof(header)); + nbytes = pread(fd, lwp, size, sizeof(header)); if (nbytes == -1) { PyErr_SetFromErrno(PyExc_OSError); goto error; @@ -401,20 +402,15 @@ psutil_proc_cpu_num(PyObject *self, PyObject *args) { } // done - lwp = (lwpsinfo_t *)ptr; proc_num = lwp->pr_onpro; close(fd); - free(ptr); - free(lpsinfo); + free(lwp); return Py_BuildValue("i", proc_num); error: if (fd != -1) close(fd); - if (ptr != NULL) - free(ptr); - if (lpsinfo != NULL) - free(lpsinfo); + free(lwp); return NULL; } @@ -853,7 +849,7 @@ psutil_proc_memory_maps(PyObject *self, PyObject *args) { int fd = -1; char path[1000]; char perms[10]; - char *name; + const char *name; struct stat st; pstatus_t status; @@ -1115,11 +1111,11 @@ static PyObject * psutil_net_connections(PyObject *self, PyObject *args) { long pid; int sd = 0; - mib2_tcpConnEntry_t *tp = NULL; - mib2_udpEntry_t *ude; + mib2_tcpConnEntry_t tp; + mib2_udpEntry_t ude; #if defined(AF_INET6) - mib2_tcp6ConnEntry_t *tp6; - mib2_udp6Entry_t *ude6; + mib2_tcp6ConnEntry_t tp6; + mib2_udp6Entry_t ude6; #endif char buf[512]; int i, flags, getcode, num_ent, state; @@ -1128,10 +1124,10 @@ psutil_net_connections(PyObject *self, PyObject *args) { int processed_pid; int databuf_init = 0; struct strbuf ctlbuf, databuf; - struct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf; - struct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf; - struct T_error_ack *tea = (struct T_error_ack *)buf; - struct opthdr *mibhdr; + struct T_optmgmt_req tor = {0}; + struct T_optmgmt_ack toa = {0}; + struct T_error_ack tea = {0}; + struct opthdr mibhdr = {0}; PyObject *py_retlist = PyList_New(0); PyObject *py_tuple = NULL; @@ -1164,22 +1160,23 @@ psutil_net_connections(PyObject *self, PyObject *args) { // which copied and pasted it from netstat source code, mibget() // function. Also see: // http://stackoverflow.com/questions/8723598/ - tor->PRIM_type = T_SVR4_OPTMGMT_REQ; - tor->OPT_offset = sizeof (struct T_optmgmt_req); - tor->OPT_length = sizeof (struct opthdr); - tor->MGMT_flags = T_CURRENT; - mibhdr = (struct opthdr *)&tor[1]; - mibhdr->level = MIB2_IP; - mibhdr->name = 0; + tor.PRIM_type = T_SVR4_OPTMGMT_REQ; + tor.OPT_offset = sizeof (struct T_optmgmt_req); + tor.OPT_length = sizeof (struct opthdr); + tor.MGMT_flags = T_CURRENT; + mibhdr.level = MIB2_IP; + mibhdr.name = 0; #ifdef NEW_MIB_COMPLIANT - mibhdr->len = 1; + mibhdr.len = 1; #else - mibhdr->len = 0; + mibhdr.len = 0; #endif + memcpy(buf, &tor, sizeof tor); + memcpy(buf + sizeof tor, &mibhdr, sizeof mibhdr); ctlbuf.buf = buf; - ctlbuf.len = tor->OPT_offset + tor->OPT_length; + ctlbuf.len = tor.OPT_offset + tor.OPT_length; flags = 0; // request to be sent in non-priority if (putmsg(sd, &ctlbuf, (struct strbuf *)0, flags) == -1) { @@ -1187,37 +1184,38 @@ psutil_net_connections(PyObject *self, PyObject *args) { goto error; } - mibhdr = (struct opthdr *)&toa[1]; ctlbuf.maxlen = sizeof (buf); for (;;) { flags = 0; getcode = getmsg(sd, &ctlbuf, (struct strbuf *)0, &flags); + memcpy(&toa, buf, sizeof toa); + memcpy(&tea, buf, sizeof tea); if (getcode != MOREDATA || - ctlbuf.len < sizeof (struct T_optmgmt_ack) || - toa->PRIM_type != T_OPTMGMT_ACK || - toa->MGMT_flags != T_SUCCESS) + ctlbuf.len < (int)sizeof (struct T_optmgmt_ack) || + toa.PRIM_type != T_OPTMGMT_ACK || + toa.MGMT_flags != T_SUCCESS) { break; } - if (ctlbuf.len >= sizeof (struct T_error_ack) && - tea->PRIM_type == T_ERROR_ACK) + if (ctlbuf.len >= (int)sizeof (struct T_error_ack) && + tea.PRIM_type == T_ERROR_ACK) { PyErr_SetString(PyExc_RuntimeError, "ERROR_ACK"); goto error; } if (getcode == 0 && - ctlbuf.len >= sizeof (struct T_optmgmt_ack) && - toa->PRIM_type == T_OPTMGMT_ACK && - toa->MGMT_flags == T_SUCCESS) + ctlbuf.len >= (int)sizeof (struct T_optmgmt_ack) && + toa.PRIM_type == T_OPTMGMT_ACK && + toa.MGMT_flags == T_SUCCESS) { PyErr_SetString(PyExc_RuntimeError, "ERROR_T_OPTMGMT_ACK"); goto error; } - databuf.maxlen = mibhdr->len; + databuf.maxlen = mibhdr.len; databuf.len = 0; - databuf.buf = (char *)malloc((int)mibhdr->len); + databuf.buf = (char *)malloc((int)mibhdr.len); if (!databuf.buf) { PyErr_NoMemory(); goto error; @@ -1232,22 +1230,22 @@ psutil_net_connections(PyObject *self, PyObject *args) { } // TCPv4 - if (mibhdr->level == MIB2_TCP && mibhdr->name == MIB2_TCP_13) { - tp = (mib2_tcpConnEntry_t *)databuf.buf; - num_ent = mibhdr->len / sizeof(mib2_tcpConnEntry_t); - for (i = 0; i < num_ent; i++, tp++) { + if (mibhdr.level == MIB2_TCP && mibhdr.name == MIB2_TCP_13) { + num_ent = mibhdr.len / sizeof(mib2_tcpConnEntry_t); + for (i = 0; i < num_ent; i++) { + memcpy(&tp, databuf.buf + i * sizeof tp, sizeof tp); #ifdef NEW_MIB_COMPLIANT - processed_pid = tp->tcpConnCreationProcess; + processed_pid = tp.tcpConnCreationProcess; #else processed_pid = 0; #endif if (pid != -1 && processed_pid != pid) continue; // construct local/remote addresses - inet_ntop(AF_INET, &tp->tcpConnLocalAddress, lip, sizeof(lip)); - inet_ntop(AF_INET, &tp->tcpConnRemAddress, rip, sizeof(rip)); - lport = tp->tcpConnLocalPort; - rport = tp->tcpConnRemPort; + inet_ntop(AF_INET, &tp.tcpConnLocalAddress, lip, sizeof(lip)); + inet_ntop(AF_INET, &tp.tcpConnRemAddress, rip, sizeof(rip)); + lport = tp.tcpConnLocalPort; + rport = tp.tcpConnRemPort; // contruct python tuple/list py_laddr = Py_BuildValue("(si)", lip, lport); @@ -1260,7 +1258,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { } if (!py_raddr) goto error; - state = tp->tcpConnEntryInfo.ce_state; + state = tp.tcpConnEntryInfo.ce_state; // add item py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET, SOCK_STREAM, @@ -1275,24 +1273,24 @@ psutil_net_connections(PyObject *self, PyObject *args) { } #if defined(AF_INET6) // TCPv6 - else if (mibhdr->level == MIB2_TCP6 && mibhdr->name == MIB2_TCP6_CONN) + else if (mibhdr.level == MIB2_TCP6 && mibhdr.name == MIB2_TCP6_CONN) { - tp6 = (mib2_tcp6ConnEntry_t *)databuf.buf; - num_ent = mibhdr->len / sizeof(mib2_tcp6ConnEntry_t); + num_ent = mibhdr.len / sizeof(mib2_tcp6ConnEntry_t); - for (i = 0; i < num_ent; i++, tp6++) { + for (i = 0; i < num_ent; i++) { + memcpy(&tp6, databuf.buf + i * sizeof tp6, sizeof tp6); #ifdef NEW_MIB_COMPLIANT - processed_pid = tp6->tcp6ConnCreationProcess; + processed_pid = tp6.tcp6ConnCreationProcess; #else processed_pid = 0; #endif if (pid != -1 && processed_pid != pid) continue; // construct local/remote addresses - inet_ntop(AF_INET6, &tp6->tcp6ConnLocalAddress, lip, sizeof(lip)); - inet_ntop(AF_INET6, &tp6->tcp6ConnRemAddress, rip, sizeof(rip)); - lport = tp6->tcp6ConnLocalPort; - rport = tp6->tcp6ConnRemPort; + inet_ntop(AF_INET6, &tp6.tcp6ConnLocalAddress, lip, sizeof(lip)); + inet_ntop(AF_INET6, &tp6.tcp6ConnRemAddress, rip, sizeof(rip)); + lport = tp6.tcp6ConnLocalPort; + rport = tp6.tcp6ConnRemPort; // contruct python tuple/list py_laddr = Py_BuildValue("(si)", lip, lport); @@ -1304,7 +1302,7 @@ psutil_net_connections(PyObject *self, PyObject *args) { py_raddr = Py_BuildValue("()"); if (!py_raddr) goto error; - state = tp6->tcp6ConnEntryInfo.ce_state; + state = tp6.tcp6ConnEntryInfo.ce_state; // add item py_tuple = Py_BuildValue("(iiiNNiI)", -1, AF_INET6, SOCK_STREAM, @@ -1318,13 +1316,13 @@ psutil_net_connections(PyObject *self, PyObject *args) { } #endif // UDPv4 - else if (mibhdr->level == MIB2_UDP || mibhdr->level == MIB2_UDP_ENTRY) { - ude = (mib2_udpEntry_t *)databuf.buf; - num_ent = mibhdr->len / sizeof(mib2_udpEntry_t); - assert(num_ent * sizeof(mib2_udpEntry_t) == mibhdr->len); - for (i = 0; i < num_ent; i++, ude++) { + else if (mibhdr.level == MIB2_UDP || mibhdr.level == MIB2_UDP_ENTRY) { + num_ent = mibhdr.len / sizeof(mib2_udpEntry_t); + assert(num_ent * sizeof(mib2_udpEntry_t) == mibhdr.len); + for (i = 0; i < num_ent; i++) { + memcpy(&ude, databuf.buf + i * sizeof ude, sizeof ude); #ifdef NEW_MIB_COMPLIANT - processed_pid = ude->udpCreationProcess; + processed_pid = ude.udpCreationProcess; #else processed_pid = 0; #endif @@ -1337,8 +1335,8 @@ psutil_net_connections(PyObject *self, PyObject *args) { // to do other than skipping. if (processed_pid > 131072) continue; - inet_ntop(AF_INET, &ude->udpLocalAddress, lip, sizeof(lip)); - lport = ude->udpLocalPort; + inet_ntop(AF_INET, &ude.udpLocalAddress, lip, sizeof(lip)); + lport = ude.udpLocalPort; py_laddr = Py_BuildValue("(si)", lip, lport); if (!py_laddr) goto error; @@ -1357,21 +1355,21 @@ psutil_net_connections(PyObject *self, PyObject *args) { } #if defined(AF_INET6) // UDPv6 - else if (mibhdr->level == MIB2_UDP6 || - mibhdr->level == MIB2_UDP6_ENTRY) + else if (mibhdr.level == MIB2_UDP6 || + mibhdr.level == MIB2_UDP6_ENTRY) { - ude6 = (mib2_udp6Entry_t *)databuf.buf; - num_ent = mibhdr->len / sizeof(mib2_udp6Entry_t); - for (i = 0; i < num_ent; i++, ude6++) { + num_ent = mibhdr.len / sizeof(mib2_udp6Entry_t); + for (i = 0; i < num_ent; i++) { + memcpy(&ude6, databuf.buf + i * sizeof ude6, sizeof ude6); #ifdef NEW_MIB_COMPLIANT - processed_pid = ude6->udp6CreationProcess; + processed_pid = ude6.udp6CreationProcess; #else processed_pid = 0; #endif if (pid != -1 && processed_pid != pid) continue; - inet_ntop(AF_INET6, &ude6->udp6LocalAddress, lip, sizeof(lip)); - lport = ude6->udp6LocalPort; + inet_ntop(AF_INET6, &ude6.udp6LocalAddress, lip, sizeof(lip)); + lport = ude6.udp6LocalPort; py_laddr = Py_BuildValue("(si)", lip, lport); if (!py_laddr) goto error; @@ -1421,7 +1419,7 @@ psutil_boot_time(PyObject *self, PyObject *args) { } } endutxent(); - if (boot_time == 0.0) { + if (fabs(boot_time) < 0.000001) { /* could not find BOOT_TIME in getutxent loop */ PyErr_SetString(PyExc_RuntimeError, "can't determine boot time"); return NULL;