From: =?utf-8?b?0L3QsNCx?= <nabijaczleweli@nabijaczleweli.xyz>
Date: Mon, 9 Dec 2024 18:09:23 +0100
Subject: Remove dependency on PATH_MAX. Don't overflow static buffer if input
 line longer than PATH_MAX. Don't change xecutable-searching behaviour if -v4

Forwarded: no
---
 jobctrl.c  |  13 ++++---
 settings.c |  30 ++++++--------
 support.c  | 129 +++++++++++++++++--------------------------------------------
 xjobs.c    |   9 +----
 4 files changed, 55 insertions(+), 126 deletions(-)

diff --git a/jobctrl.c b/jobctrl.c
index 066636e..f150407 100644
--- a/jobctrl.c
+++ b/jobctrl.c
@@ -404,7 +404,7 @@ static void exec_job(job_t *j)
 {
 	static unsigned jobid = 0;
 	int in = -1, out = -1, err = -1, log = -1;
-	char pwd[PATH_MAX];
+	char *pwd = NULL;
 #ifdef HAVE_SPAWN
 	posix_spawn_file_actions_t acts;
 
@@ -415,15 +415,14 @@ static void exec_job(job_t *j)
 	if (ask_exec(j))
 		return;
 	if (j->pwd) {
-		char *p = getcwd(pwd,sizeof(pwd));
-		assert(p);
+		pwd = getcwd(NULL,0);
+		assert(pwd);
 		if (-1 == chdir(j->pwd)) {
 			warn("cannot change directory to %s for job %u: %s\n",j->pwd,jobid,strerror(errno));
 			j->pid = -1;
 			goto finish;
 		}
-	} else
-		pwd[0] = 0;
+	}
 	j->start = gettime();
 	if ((j->in == 0) && (JobIn != 0))
 		j->in = Strdup(JobIn);
@@ -433,6 +432,7 @@ static void exec_job(job_t *j)
 		if (in == -1) {
 			warn("cannot open input file %s for job %u: %s\n",j->in,jobid,strerror(errno));
 			j->pid = -1;
+			free(pwd);
 			return;
 		}
 		dbug("job input redirected to %s\n",j->in);
@@ -564,8 +564,9 @@ finish:
 		(void) close(out);
 	if ((err != -1) && (err != Stderr) && (err != out) && (err != log))
 		(void) close(err);
-	if ((pwd[0] != 0) && (chdir(pwd) == -1))
+	if (pwd && (chdir(pwd) == -1))
 		error("unable to change back to working directory %s: %s\n",pwd,strerror(errno));
+	free(pwd);
 }
 
 
diff --git a/settings.c b/settings.c
index e49c262..44c9703 100644
--- a/settings.c
+++ b/settings.c
@@ -17,6 +17,7 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define _GNU_SOURCE
 #include <errno.h>
 #include <limits.h>
 #include <math.h>
@@ -48,11 +49,6 @@
 #include <sys/sysctl.h>
 #endif
 
-/* SUSv3 does not have PATH_MAX */
-#ifndef PATH_MAX
-#define PATH_MAX _XOPEN_PATH_MAX
-#endif
-
 extern int yylex(void);
 
 int (*gettoken)(void) = yylex;
@@ -341,10 +337,9 @@ void init_defaults(const char *exe)
 		Pagesize = 4096;
 	} else
 		Pagesize = ps;
-	char cfname[PATH_MAX+1];
-	if (0 != getcwd(cfname,sizeof(cfname))) {
+	char *cfname, *cfpath;
+	if ((cfname = getcwd(NULL,0))) {
 		size_t cl = strlen(cfname);
-		assert(cl < sizeof(cfname)-1);
 		if (cfname[cl-1] != '/') {
 			cfname[cl++] = '/';
 			cfname[cl] = 0;
@@ -352,20 +347,17 @@ void init_defaults(const char *exe)
 		if ((exe[0] == '.') && (exe[1] == '/'))
 			exe += 2;
 		size_t el = strlen(exe);
-		assert(cl+el < sizeof(cfname));
-		memcpy(cfname+cl,exe,el+1);
-		char *sl = strrchr(cfname,'/');
-		assert((sl-cfname)+17<sizeof(cfname));
-		memcpy(sl+1,"../etc/xjobs.rc",16);
+		asprintf(&cfpath, "%s%.*s", cfname, (int)el+1, exe);
+		char *sl = strrchr(cfpath,'/');
+		asprintf(&cfname, "%.*s../etc/xjobs.rc", (int)(sl - cfpath + 1), cfpath);
 		read_config(cfname);
+		free(cfname);
+		free(cfpath);
 	}
 	const char *home = getenv("HOME");
-	if (home) {
-		size_t hl = strlen(home);
-		assert(hl+11 <= sizeof(cfname));
-		memcpy(cfname,home,hl);
-		memcpy(cfname+hl,"/.xjobs.rc",11);
+	if (home && asprintf(&cfname, "%s/.xjobs.rc", home) != -1) {
 		read_config(cfname);
+		free(cfname);
 	}
 }
 
@@ -452,7 +444,7 @@ void parse_options(int argc, char **argv)
 		case 'h':
 			usage();
 			break;
-		case 'j': 
+		case 'j':
 			parse_option_j(optarg);
 			break;
 		case 'L':
diff --git a/support.c b/support.c
index 3d08a6c..e151b97 100644
--- a/support.c
+++ b/support.c
@@ -40,8 +40,8 @@ typedef struct cache_s {
 } cache_t;
 
 static cache_t *Cache = 0;
-static char Token[PATH_MAX+1] = "", PWD[PATH_MAX];
-static size_t PwdLen = 0;
+static char *Token;
+static size_t TokenCap;
 
 
 void *Malloc(size_t s)
@@ -180,96 +180,44 @@ char *replace_string_l(char *in, const char *param, long v)
 }
 
 
-static char get_stdin_char(void)
+int read_to_char(int delim)
 {
-	static char buf[8*1024];
-	static char *at = buf, *end = buf;
-	if (at == end) {
-		int num = read(STDIN_FILENO,buf,sizeof(buf));
-		if (-1 == num) {
-			fprintf(stderr,"error reading stdin: %s\n",strerror(errno));
-			return 0;
-		} else if (0 == num) {
-			return 0;
-		} else {
-			at = buf;
-			end = buf+num;
-		}
-	}
-	return *at++;
-}
-
-
-/* not thread-safe */
-int read_to_0(void)
-{
-	char *b = Token, c;
-	if (Token[0] != 0) {
-		Token[0] = 0;
+	if (TokenCap == (size_t)-1)
+		return 0;
+	if (Token) {
+		free(Token);
+		Token = NULL;
+		TokenCap = 0;
 		return EOL;
 	}
-	do {
-		c = get_stdin_char();
-		*b = c;
-		++b;
-		assert(b-Token < sizeof(Token));
-	} while (c);
-	if (*Token == 0)
+
+	ssize_t rd = getdelim(&Token, &TokenCap, delim, stdin);
+	if (rd == -1) {
+		free(Token);
+		Token = NULL;
+		TokenCap = (size_t)-1;
 		return 0;
-	Fill = b - Token;
+	}
+
+	if (Token[rd - 1] == delim)
+		Token[--rd] = '\0';
+
 	Buffer = Token;
+	Fill = rd;
 	return UQUOTED;
 }
 
-
 /* not thread-safe */
-int read_to_nl(void)
+int read_to_0(void)
 {
-	char *b = Token, c;
-	if (Token[0] != 0) {
-		Token[0] = 0;
-		return EOL;
-	}
-	do {
-		c = get_stdin_char();
-		*b = c;
-		++b;
-		assert(b-Token < sizeof(Token));
-	} while (c && (c != '\n'));
-	if (*Token == 0)
-		return 0;
-	*--b = 0;
-	Fill = b - Token;
-	Buffer = Token;
-	return UQUOTED;
+	return read_to_char('\0');
 }
 
 
-int resolve_symlink(char *cmd)
+/* not thread-safe */
+int read_to_nl(void)
 {
-	char lt[PATH_MAX];
-	ssize_t l;
-	/* handle symlinks */
-	bzero(lt,sizeof(lt));
-	while (-1 != (l = readlink(cmd,lt,PATH_MAX))) {
-		char *base = cmd;
-		lt[l] = '\0';
-		dbug("is symlink: %s\n",lt);
-		/* handle relative links */
-		if (lt[0] != '/') {
-			base = strrchr(cmd,'/');   
-			if (base == 0) {
-				base = cmd;
-			} else if ((l + (base - cmd)) > PATH_MAX) {
-				warn("command exceed PATH_MAX limit of your system\n");
-				return -1;
-			}
-			++base;
-		}
-		(void) memcpy(base,lt,l+1);
-		dbug("resolved to: %s\n",cmd);
-	}
-	return 0;
+	return read_to_char('\n');
 }
 
 
@@ -323,19 +271,13 @@ char *complete_exe(char *exe)
 		if (0 == Path)
 			Path = strdup(_PATH_STDPATH);
 		dbug("PATH=\"%s\"\n", Path);
-		if (0 == getcwd(PWD,sizeof(PWD)))
+		char *PWD = getcwd(NULL, 0);
+		if (!PWD)
 			error("current work directory exceeds maximum allowed size\n");
 		/* on Linux for chroot environments */
 		if (0 == strncmp(PWD,"(unreachable)",13))
 			error("current work directory is unreachable\n");
-		PwdLen = strlen(PWD);
-		if (PWD[PwdLen-1] != '/') {
-			if (PwdLen >= sizeof(PWD)-1)
-				error("current work directory exceeds maximum allowed size\n");
-			PWD[PwdLen] = '/';
-			++PwdLen;
-			PWD[PwdLen] = 0;
-		}
+		free(PWD);
 	}
 	cache_t *c = Cache;
 	while (c) {
@@ -356,14 +298,17 @@ char *complete_exe(char *exe)
 		// relative path we need to resolve over PWD
 		if (0 == is_executable(exe))
 			return add_cache(exe,0);
-		char cmd[PATH_MAX];
-		if (0 == realpath(exe,cmd)) {
+		char *cmd;
+		if (!(cmd == realpath(exe,NULL))) {
 			warn("unable to resolve %s: %s\n",exe,strerror(errno));
 			return 0;
 		}
 		dbug("%s resolved to: %s\n",exe,cmd);
-		if (0 == is_executable(cmd))
+		if (0 == is_executable(cmd)) {
+			free(cmd);
 			return add_cache(exe,cmd);
+		}
+		free(cmd);
 		return 0;
 	}
 
@@ -374,8 +319,6 @@ char *complete_exe(char *exe)
 		if (next)
 			*next = 0;
 		size_t off = strlen(token);
-		if (exel + off >= PATH_MAX)
-			warn("command exceeds PATH_MAX limit of your system\n");
 		char cmd[off+exel+2];
 		(void) memcpy(cmd, token, off);
 		if (cmd[off-1] != '/') {
@@ -389,8 +332,6 @@ char *complete_exe(char *exe)
 			next = strchr(token,':');
 		} else
 			token = 0;
-		if ((Verbose == Debug) && resolve_symlink(cmd))
-			continue;
 		dbug("check via PATH '%s' resolved to: %s\n",exe,cmd);
 		if (0 == is_executable(cmd)) {
 			return add_cache(exe,cmd);
diff --git a/xjobs.c b/xjobs.c
index 30975a0..f76a5f7 100644
--- a/xjobs.c
+++ b/xjobs.c
@@ -43,11 +43,6 @@
 #endif
 
 
-/* SUSv3 does not have PATH_MAX */
-#ifndef PATH_MAX
-#define PATH_MAX _XOPEN_PATH_MAX
-#endif
-
 #if __STDC_VERSION__ < 199901L
 #define inline
 #endif
@@ -191,7 +186,7 @@ void parse_args(int argc, char **argv)
 				++gt;
 			else if (*a == '&')
 				++as;
-			else 
+			else
 				e = 1;
 			++a;
 		} while ((e == 0) && (*a));
@@ -248,7 +243,7 @@ static void wait_and_clear(int h)
 	if (RsrcUsage) {
 		struct rusage rusage;
 		pid_t pid = wait3(&ret,flags,&rusage);
-		if (0 < pid) 
+		if (0 < pid)
 			clear_job(pid,ret,&rusage);
 		else
 			dbug("wait4(%u,%u,%x): %s\n",Waiting,QLen,flags,strerror(errno));
