From: Karel Zak Date: Mon, 1 Jun 2026 09:23:57 +0000 (+0200) Subject: readprofile: replace popen() with fork/exec for .gz map files X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=a252e90a878f1c3afc2b6f2c1378094cb6af66a5;p=thirdparty%2Futil-linux.git readprofile: replace popen() with fork/exec for .gz map files Security scanners repeatedly flag the popen("zcat %s", name) pattern as a command injection vulnerability (CWE-78). While this is a false positive -- readprofile is not installed with elevated privileges and the filename comes from the user's own command line -- the reports are a recurring nuisance. The root cause is that popen() passes the command through /bin/sh, which makes scanners flag it regardless of whether the input is actually untrusted. Replace popen() with fork()/execlp() to invoke zcat directly without shell interpretation. This eliminates the shell from the execution path and silences the scanners without adding any new dependencies. Also use ul_endswith() for the .gz suffix check, and handle fdopen() failure after fork to avoid fd leak and zombie process. Signed-off-by: Karel Zak --- diff --git a/sys-utils/readprofile.c b/sys-utils/readprofile.c index 0165a3373..76b43f919 100644 --- a/sys-utils/readprofile.c +++ b/sys-utils/readprofile.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include "c.h" @@ -56,21 +57,44 @@ static char defaultmap[]="/boot/System.map"; static char defaultpro[]="/proc/profile"; -static FILE *myopen(char *name, char *mode, int *flag) +static FILE *myopen(char *name, pid_t *cpid) { - int len = strlen(name); - - if (!strcmp(name + len - 3, ".gz")) { - FILE *res; - char *cmdline = xmalloc(len + 6); - snprintf(cmdline, len + 6, "zcat %s", name); - res = popen(cmdline, mode); - free(cmdline); - *flag = 1; - return res; + *cpid = 0; + + if (ul_endswith(name, ".gz")) { + int fd[2]; + pid_t pid; + FILE *fp; + + if (pipe(fd) != 0) + err(EXIT_FAILURE, _("pipe failed")); + + pid = fork(); + if (pid < 0) + err(EXIT_FAILURE, _("fork failed")); + + if (pid == 0) { + close(fd[0]); + if (fd[1] != STDOUT_FILENO) { + dup2(fd[1], STDOUT_FILENO); + close(fd[1]); + } + execlp("zcat", "zcat", name, NULL); + err(EXIT_FAILURE, _("failed to execute zcat")); + } + + close(fd[1]); + fp = fdopen(fd[0], "r"); + if (!fp) { + close(fd[0]); + waitpid(pid, NULL, 0); + return NULL; + } + *cpid = pid; + return fp; } - *flag = 0; - return fopen(name, mode); + + return fopen(name, "r"); } #ifndef BOOT_SYSTEM_MAP @@ -137,7 +161,7 @@ int main(int argc, char **argv) int optBins = 0, optSub = 0; char mapline[S_LEN]; int maplineno = 1; - int popenMap; /* flag to tell if popen() has been used */ + pid_t gzip_pid; int header_printed; double rep = 0; @@ -283,10 +307,10 @@ int main(int argc, char **argv) total = 0; - map = myopen(mapFile, "r", &popenMap); + map = myopen(mapFile, &gzip_pid); if (map == NULL && mapFile == defaultmap) { mapFile = boot_uname_r_str(); - map = myopen(mapFile, "r", &popenMap); + map = myopen(mapFile, &gzip_pid); } if (map == NULL) err(EXIT_FAILURE, "%s", mapFile); @@ -400,6 +424,8 @@ int main(int argc, char **argv) printf("%6u %-40s %8.4f\n", total, _("total"), rep); - popenMap ? pclose(map) : fclose(map); + fclose(map); + if (gzip_pid > 0) + waitpid(gzip_pid, NULL, 0); exit(EXIT_SUCCESS); }