library: ensure thread safety for all static variables

Even though we we had to abandon the master branch top
multi-thread effort and even though the newlib version
of a multi-threaded top provides no real benefit, that
whole exercise was not wasted. Rather, it has revealed
some deficiencies in our library which this addresses.

If two or more threads in the same address space tried
to access the same api simultaneously, there is a good
chance some function-local static variables will yield
some of those renowned unpredictable results. So, this
patch protects them with the '__thread' storage class.

Reference(s):
https://www.freelists.org/post/procps/a-few-more-patches,7

Signed-off-by: Jim Warner <james.warner@comcast.net>
This commit is contained in:
Jim Warner 2021-09-28 00:00:00 -05:00 committed by Craig Small
parent 69978e3650
commit 23cfb71366
10 changed files with 28 additions and 28 deletions

View File

@ -325,7 +325,7 @@ static int ctty_name(char *restrict const buf, int pid) {
/* number --> name */
unsigned dev_to_tty(char *restrict ret, unsigned chop, dev_t dev_t_dev, int pid, unsigned int flags) {
static char buf[TTY_NAME_SIZE];
static __thread char buf[TTY_NAME_SIZE];
char *restrict tmp = buf;
unsigned dev = dev_t_dev;
unsigned i = 0;

View File

@ -91,7 +91,7 @@ static inline void esc_ctl (unsigned char *str, int len) {
}
int escape_str (unsigned char *dst, const unsigned char *src, int bufsize) {
static int utf_sw = 0;
static __thread int utf_sw = 0;
int n;
if (utf_sw == 0) {

View File

@ -669,7 +669,7 @@ static int meminfo_read_failed (
head = buf;
for (;;) {
static ENTRY e; // just to keep coverity off our backs (e.data)
static __thread ENTRY e; // keep coverity off our backs (e.data)
ENTRY *ep;
if (!(tail = strchr(head, ':')))
@ -882,7 +882,7 @@ PROCPS_EXPORT struct meminfo_result *procps_meminfo_get (
struct meminfo_info *info,
enum meminfo_item item)
{
static time_t sav_secs;
static __thread time_t sav_secs;
time_t cur_secs;
errno = EINVAL;

View File

@ -1123,7 +1123,7 @@ static int pids_stacks_fetch (
#define n_alloc info->fetch.n_alloc
#define n_inuse info->fetch.n_inuse
#define n_saved info->fetch.n_alloc_save
static proc_t task; // static for initial zeroes + later dynamic free(s)
static __thread proc_t task; // static for initial 0's + later free(s)
struct stacks_extent *ext;
// initialize stuff -----------------------------------
@ -1367,7 +1367,7 @@ PROCPS_EXPORT struct pids_stack *fatal_proc_unmounted (
struct pids_info *info,
int return_self)
{
static proc_t self;
static __thread proc_t self;
struct stacks_extent *ext;
/* this is very likely the *only* newlib function where the
@ -1404,7 +1404,7 @@ PROCPS_EXPORT struct pids_stack *procps_pids_get (
struct pids_info *info,
enum pids_fetch_type which)
{
static proc_t task; // static for initial zeroes + later dynamic free(s)
static __thread proc_t task; // static for initial 0's + later free(s)
errno = EINVAL;
if (info == NULL)

View File

@ -965,7 +965,7 @@ static int fill_environ_cvt (const char *directory, proc_t *restrict p) {
// tracking all names already seen thus avoiding the overhead of repeating
// malloc() and free() calls.
static char *lxc_containers (const char *path) {
static struct utlbuf_s ub = { NULL, 0 }; // util buffer for whole cgroup
static __thread struct utlbuf_s ub = { NULL, 0 }; // util buffer for whole cgroup
static char lxc_none[] = "-";
static char lxc_oops[] = "?"; // used when memory alloc fails
/*
@ -995,7 +995,7 @@ static char *lxc_containers (const char *path) {
if ((p1 = strstr(ub.buf, (delim = lxc_delm1)))
|| ((p1 = strstr(ub.buf, (delim = lxc_delm2)))
|| ((p1 = strstr(ub.buf, (delim = lxc_delm3)))))) {
static struct lxc_ele {
static __thread struct lxc_ele {
struct lxc_ele *next;
char *name;
} *anchor = NULL;
@ -1112,8 +1112,8 @@ static void autogroup_fill (const char *path, proc_t *p) {
// The pid (tgid? tid?) is already in p, and a path to it in path, with some
// room to spare.
static proc_t *simple_readproc(PROCTAB *restrict const PT, proc_t *restrict const p) {
static struct utlbuf_s ub = { NULL, 0 }; // buf for stat,statm,status
static struct stat sb; // stat() buffer
static __thread struct utlbuf_s ub = { NULL, 0 }; // buf for stat,statm,status
static __thread struct stat sb; // stat() buffer
char *restrict const path = PT->path;
unsigned flags = PT->flags;
int rc = 0;
@ -1235,8 +1235,8 @@ next_proc:
// t is the POSIX thread (task group member, generally not the leader)
// path is a path to the task, with some room to spare.
static proc_t *simple_readtask(PROCTAB *restrict const PT, proc_t *restrict const t, char *restrict const path) {
static struct utlbuf_s ub = { NULL, 0 }; // buf for stat,statm,status
static struct stat sb; // stat() buffer
static __thread struct utlbuf_s ub = { NULL, 0 }; // buf for stat,statm,status
static __thread struct stat sb; // stat() buffer
unsigned flags = PT->flags;
int rc = 0;
@ -1356,7 +1356,7 @@ next_task:
// This finds processes in /proc in the traditional way.
// Return non-zero on success.
static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
static struct dirent *ent; /* dirent handle */
static __thread struct dirent *ent; /* dirent handle */
char *restrict const path = PT->path;
for (;;) {
ent = readdir(PT->procfs);
@ -1374,7 +1374,7 @@ static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p)
// This finds tasks in /proc/*/task/ in the traditional way.
// Return non-zero on success.
static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {
static struct dirent *ent; /* dirent handle */
static __thread struct dirent *ent; /* dirent handle */
if(PT->taskdir_user != p->tgid){
if(PT->taskdir){
closedir(PT->taskdir);
@ -1402,7 +1402,7 @@ static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict con
// This "finds" processes in a list that was given to openproc().
// Return non-zero on success. (tgid is a real headache)
static int listed_nextpid (PROCTAB *PT, proc_t *p) {
static struct utlbuf_s ub = { NULL, 0 };
static __thread struct utlbuf_s ub = { NULL, 0 };
pid_t pid = *(PT->pids)++;
char *path = PT->path;
@ -1461,9 +1461,9 @@ out:
// the next unique process or task available. If no more are available,
// return a null pointer (boolean false).
proc_t *readeither (PROCTAB *restrict const PT, proc_t *restrict x) {
static proc_t skel_p; // skeleton proc_t, only uses tid + tgid
static proc_t *new_p; // for process/task transitions
static int canary, leader;
static __thread proc_t skel_p; // skeleton proc_t, only uses tid + tgid
static __thread proc_t *new_p; // for process/task transitions
static __thread int canary, leader;
char path[PROCPATHLEN];
proc_t *ret;
@ -1511,7 +1511,7 @@ end_procs:
PROCTAB *openproc(unsigned flags, ...) {
va_list ap;
struct stat sbuf;
static int did_stat;
static __thread int did_stat;
PROCTAB *PT = calloc(1, sizeof(PROCTAB));
if (!PT)

View File

@ -839,7 +839,7 @@ PROCPS_EXPORT struct slabinfo_result *procps_slabinfo_get (
struct slabinfo_info *info,
enum slabinfo_item item)
{
static time_t sav_secs;
static __thread time_t sav_secs;
time_t cur_secs;
errno = EINVAL;

View File

@ -990,7 +990,7 @@ PROCPS_EXPORT struct stat_result *procps_stat_get (
struct stat_info *info,
enum stat_item item)
{
static time_t sav_secs;
static __thread time_t sav_secs;
time_t cur_secs;
errno = EINVAL;

View File

@ -49,14 +49,14 @@ static int loadavg_fd = -1;
// and would need 1258 if the obsolete fields were there.
// As of 3.13 /proc/vmstat needs 2623,
// and /proc/stat needs 3076.
static char buf[8192];
static __thread char buf[8192];
/* This macro opens filename only if necessary and seeks to 0 so
* that successive calls to the functions are more efficient.
* It also reads the current contents of the file into the global buf.
*/
#define FILE_TO_BUF(filename, fd) do{ \
static int local_n; \
static __thread int local_n; \
if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \
fputs(BAD_OPEN_MESSAGE, stderr); \
fflush(NULL); \
@ -158,7 +158,7 @@ PROCPS_EXPORT unsigned int procps_pid_length(void)
{
FILE *fp;
char pidbuf[24];
static int pid_length=0;
static __thread int pid_length=0;
if (pid_length)
return pid_length;

View File

@ -1193,7 +1193,7 @@ static int vmstat_read_failed (
head = buf;
for (;;) {
static ENTRY e; // just to keep coverity off our backs (e.data)
static __thread ENTRY e; // keep coverity off our backs (e.data)
ENTRY *ep;
if (!(tail = strchr(head, ' ')))
@ -1376,7 +1376,7 @@ PROCPS_EXPORT struct vmstat_result *procps_vmstat_get (
struct vmstat_info *info,
enum vmstat_item item)
{
static time_t sav_secs;
static __thread time_t sav_secs;
time_t cur_secs;
errno = EINVAL;

View File

@ -27,7 +27,7 @@
const char *lookup_wchan (int pid) {
static char buf[64];
static __thread char buf[64];
const char *ret = buf;
ssize_t num;
int fd;