]> granicus.if.org Git - procps-ng/commitdiff
library: ensure thread safety via function substitutes
authorJim Warner <james.warner@comcast.net>
Tue, 28 Sep 2021 05:00:00 +0000 (00:00 -0500)
committerCraig Small <csmall@dropbear.xyz>
Sat, 2 Oct 2021 02:55:31 +0000 (12:55 +1000)
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 use procps_loadavg or procps_uptime simultaneously,
there's a chance they would experience problems due to
thread-unsafe functions our library called internally.

So, this patch switches them for thread-safe versions.

[ along the way we will also make that procps_uptime ]
[ initialization of his 'up' & 'idle' variables mean ]
[ something by delaying the -ERANGE return a little. ]

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

Signed-off-by: Jim Warner <james.warner@comcast.net>
proc/sysinfo.c
proc/uptime.c

index 9dd0be03ce7bf77dd0b71378d437970f0231b07a..ed052221de538543e4cb4c20fc36f280192f96cc 100644 (file)
@@ -124,17 +124,17 @@ PROCPS_EXPORT int procps_loadavg(
         double *restrict av15)
 {
     double avg_1=0, avg_5=0, avg_15=0;
-    char savelocale[128];
+    locale_t tmplocale;
     int retval=0;
 
     FILE_TO_BUF(LOADAVG_FILE,loadavg_fd);
-    snprintf(savelocale, sizeof(savelocale), "%s", setlocale(LC_NUMERIC, NULL));
-    setlocale(LC_NUMERIC, "C");
-    if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) {
-        setlocale(LC_NUMERIC, savelocale);
+    tmplocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
+    uselocale(tmplocale);
+    if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3)
         retval = -ERANGE;
-    }
-    setlocale(LC_NUMERIC, savelocale);
+
+    uselocale(LC_GLOBAL_LOCALE);
+    freelocale(tmplocale);
     SET_IF_DESIRED(av1,  avg_1);
     SET_IF_DESIRED(av5,  avg_5);
     SET_IF_DESIRED(av15, avg_15);
index e2cc225e19430098c7efb06c4f8ae50dcd9819ba..0f1fab2420f94f167419b9cae9e5da633d73c17d 100644 (file)
@@ -69,25 +69,27 @@ PROCPS_EXPORT int procps_uptime(
         double *restrict idle_secs)
 {
     double up=0, idle=0;
-    char savelocale[128];
+    locale_t tmplocale;
     FILE *fp;
+    int rc;
 
     if ((fp = fopen(UPTIME_FILE, "r")) == NULL)
         return -errno;
 
-    snprintf(savelocale, sizeof(savelocale), "%s", setlocale(LC_NUMERIC, NULL));
-    setlocale(LC_NUMERIC, "C");
-    if (fscanf(fp, "%lf %lf", &up, &idle) < 2) {
-        setlocale(LC_NUMERIC, savelocale);
-        fclose(fp);
-        return -ERANGE;
-    }
+    tmplocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
+    uselocale(tmplocale);
+    rc = fscanf(fp, "%lf %lf", &up, &idle);
     fclose(fp);
-    setlocale(LC_NUMERIC, savelocale);
+    uselocale(LC_GLOBAL_LOCALE);
+    freelocale(tmplocale);
+
     if (uptime_secs)
         *uptime_secs = up;
     if (idle_secs)
         *idle_secs = idle;
+
+    if (rc < 2)
+        return -ERANGE;
     return 0;
 }
 
@@ -103,14 +105,15 @@ PROCPS_EXPORT char *procps_uptime_sprint(void)
     int upminutes, uphours, updays, users;
     int pos;
     time_t realseconds;
-    struct tm *realtime;
+    struct tm realtime;
     double uptime_secs, idle_secs;
     double av1, av5, av15;
 
     upbuf[0] = '\0';
     if (time(&realseconds) < 0)
         return upbuf;
-    realtime = localtime(&realseconds);
+    localtime_r(&realseconds, &realtime);
+
     if (procps_uptime(&uptime_secs, &idle_secs) < 0)
         return upbuf;
 
@@ -119,7 +122,7 @@ PROCPS_EXPORT char *procps_uptime_sprint(void)
     upminutes = ((int) uptime_secs / (60)) % 60;
 
     pos = sprintf(upbuf, " %02d:%02d:%02d up ",
-        realtime->tm_hour, realtime->tm_min, realtime->tm_sec);
+        realtime.tm_hour, realtime.tm_min, realtime.tm_sec);
 
     if (updays)
         pos += sprintf(upbuf + pos, "%d %s, ", updays, (updays > 1) ? "days" : "day");