Subject: bin/6794: sh(1) . (dot) command reads files in current directory if not found in the PATH
To: None <gnats-bugs@gnats.netbsd.org>
From: ITOH Yasufumi <yasufu-i@is.aist-nara.ac.jp>
List: netbsd-bugs
Date: 01/13/1999 04:43:05
>Number:         6794
>Category:       bin
>Synopsis:       sh(1) . (dot) command reads files in current directory if not found in the PATH
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Jan 12 11:50:01 1999
>Last-Modified:
>Originator:     ITOH Yasufumi
>Organization:
	Nara Institute of Science and Technology, Nara, Japan
>Release:        1.3I (Jan. 12, 1999)
>Environment:
System: NetBSD acha.my.domain 1.3I NetBSD 1.3I (ALL) #1: Sun Jan 10 03:41:50 JST 1999 itohy@myname.my.domain:/usr/current/src/sys/arch/x68k/compile/ALL x68k


>Description:

[ This PR is for problem description and public review of the change.
  I'll commit this by myself if no objection.  Comments are welcome.  ]

I found two bugs in sh(1).

 1. The . (dot) command of sh(1) reads file from current directory
    if the argument doesn't contain slashes and the named file
    is not found in PATH.
    This may be a potential security problem.

 2. sh(1) prints incorrect diagnosis on nested . commands.

>How-To-Repeat:

 1.
	% sh					# try with sh
	$ echo echo foo >foo
	$ PATH=/usr/bin:/bin			# current dir is not in path
	$ . foo
	foo
	$ ksh					# try with ksh
	$ . foo
	ksh: .: foo: not found			# I think this is correct
	$

 2.
	% sh
	$ cat foo1				# here are two files
	. foo2
	fi
	$ cat foo2
	
	$ PATH=:$PATH				# search current dir
	$ . foo1
	foo2: 2: Syntax error: "fi" unexpected	# "foo2" should be "foo1"
	$

>Fix:

Apply the patch below.

 1. Don't try to read if the file is not in the PATH.

 2. This is because the source filename is stored in a static buffer.
    I changed this to use the "string stack" of ash.

diff -uF^[a-zA-Z_][a-z 	A-Z0-9_]*(.*[^;]$ main.c.orig main.c
--- main.c.orig	Mon Jan 11 23:04:51 1999
+++ main.c	Tue Jan 12 20:41:40 1999
@@ -315,22 +315,22 @@ readcmdfile(name)
 find_dot_file(basename)
 	char *basename;
 {
-	static char localname[FILENAME_MAX+1];
 	char *fullname;
 	char *path = pathval();
 	struct stat statb;
 
 	/* don't try this for absolute or relative paths */
-	if( strchr(basename, '/'))
+	if (strchr(basename, '/'))
 		return basename;
 
 	while ((fullname = padvance(&path, basename)) != NULL) {
-		strcpy(localname, fullname);
-		stunalloc(fullname);
 		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))
-			return localname;
+			return fullname;
+		stunalloc(fullname);
 	}
-	return basename;
+
+	/* not found in the PATH */
+	return NULL;
 }
 
 int
@@ -347,10 +347,19 @@ dotcmd(argc, argv)
 	if (argc >= 2) {		/* That's what SVR2 does */
 		char *fullname = find_dot_file(argv[1]);
 
-		setinputfile(fullname, 1);
-		commandname = fullname;
-		cmdloop(0);
-		popfile();
+		if (!fullname) {
+			outfmt(&errout, "%s: not found\n", argv[1]);
+			exitstatus = 1;
+		} else {
+			/*
+			 * Don't bother free()ing fullname here, since it will
+			 * be freed in eval.c::evalcommand() after return.
+			 */
+			setinputfile(fullname, 1);
+			commandname = fullname;
+			cmdloop(0);
+			popfile();
+		}
 	}
 	return exitstatus;
 }
>Audit-Trail:
>Unformatted: