patch-2.4.0-test3 linux/fs/exec.c

Next file: linux/fs/ext2/fsync.c
Previous file: linux/fs/efs/symlink.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test2/linux/fs/exec.c linux/fs/exec.c
@@ -101,37 +101,52 @@
  */
 asmlinkage long sys_uselib(const char * library)
 {
-	int fd, retval;
 	struct file * file;
+	struct nameidata nd;
+	int error;
 
-	fd = sys_open(library, 0, 0);
-	if (fd < 0)
-		return fd;
-	file = fget(fd);
-	retval = -ENOEXEC;
-	if (file) {
-		if(file->f_op && file->f_op->read) {
-			struct linux_binfmt * fmt;
+	error = user_path_walk(library, &nd);
+	if (error)
+		goto out;
+
+	error = -EINVAL;
+	if (!S_ISREG(nd.dentry->d_inode->i_mode))
+		goto exit;
+
+	error = permission(nd.dentry->d_inode, MAY_READ | MAY_EXEC);
+	if (error)
+		goto exit;
 
-			read_lock(&binfmt_lock);
-			for (fmt = formats ; fmt ; fmt = fmt->next) {
-				if (!fmt->load_shlib)
-					continue;
-				if (!try_inc_mod_count(fmt->module))
-					continue;
-				read_unlock(&binfmt_lock);
-				retval = fmt->load_shlib(file);
-				read_lock(&binfmt_lock);
-				put_binfmt(fmt);
-				if (retval != -ENOEXEC)
-					break;
-			}
+	file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+	error = PTR_ERR(file);
+	if (IS_ERR(file))
+		goto out;
+
+	error = -ENOEXEC;
+	if(file->f_op && file->f_op->read) {
+		struct linux_binfmt * fmt;
+
+		read_lock(&binfmt_lock);
+		for (fmt = formats ; fmt ; fmt = fmt->next) {
+			if (!fmt->load_shlib)
+				continue;
+			if (!try_inc_mod_count(fmt->module))
+				continue;
 			read_unlock(&binfmt_lock);
+			error = fmt->load_shlib(file);
+			read_lock(&binfmt_lock);
+			put_binfmt(fmt);
+			if (error != -ENOEXEC)
+				break;
 		}
-		fput(file);
+		read_unlock(&binfmt_lock);
 	}
-	sys_close(fd);
-  	return retval;
+	fput(file);
+out:
+  	return error;
+exit:
+	path_release(&nd);
+	goto out;
 }
 
 /*
@@ -319,23 +334,28 @@
 struct file *open_exec(const char *name)
 {
 	struct nameidata nd;
+	struct inode *inode;
 	struct file *file;
 	int err = 0;
 
-	lock_kernel();
 	if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd))
 		err = path_walk(name, &nd);
-	unlock_kernel();
 	file = ERR_PTR(err);
 	if (!err) {
+		inode = nd.dentry->d_inode;
 		file = ERR_PTR(-EACCES);
-		if (S_ISREG(nd.dentry->d_inode->i_mode)) {
-			int err = permission(nd.dentry->d_inode, MAY_EXEC);
+		if (!IS_NOEXEC(inode) && S_ISREG(inode->i_mode)) {
+			int err = permission(inode, MAY_EXEC);
 			file = ERR_PTR(err);
 			if (!err) {
-				lock_kernel();
 				file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
-				unlock_kernel();
+				if (!IS_ERR(file)) {
+					err = deny_write_access(file);
+					if (err) {
+						fput(file);
+						file = ERR_PTR(err);
+					}
+				}
 out:
 				return file;
 			}
@@ -540,23 +560,13 @@
 int prepare_binprm(struct linux_binprm *bprm)
 {
 	int mode;
-	int retval,id_change,cap_raised;
+	int id_change,cap_raised;
 	struct inode * inode = bprm->file->f_dentry->d_inode;
 
 	mode = inode->i_mode;
-	if (!S_ISREG(mode))			/* must be regular file */
-		return -EACCES;
-	if (!(mode & 0111))			/* with at least _one_ execute bit set */
-		return -EACCES;
-	if (IS_NOEXEC(inode))			/* FS mustn't be mounted noexec */
-		return -EACCES;
-	if (!inode->i_sb)
+	/* Huh? We had already checked for MAY_EXEC, WTF do we check this? */
+	if (!(mode & 0111))	/* with at least _one_ execute bit set */
 		return -EACCES;
-	if ((retval = permission(inode, MAY_EXEC)) != 0)
-		return retval;
-	/* better not execute files which are being written to */
-	if (atomic_read(&inode->i_writecount) > 0)
-		return -ETXTBSY;
 
 	bprm->e_uid = current->euid;
 	bprm->e_gid = current->egid;
@@ -728,6 +738,7 @@
 		char * dynloader[] = { "/sbin/loader" };
 		struct file * file;
 
+		allow_write_access(bprm->file);
 		fput(bprm->file);
 		bprm->file = NULL;
 
@@ -761,6 +772,7 @@
 			retval = fn(bprm, regs);
 			if (retval >= 0) {
 				put_binfmt(fmt);
+				allow_write_access(bprm->file);
 				if (bprm->file)
 					fput(bprm->file);
 				bprm->file = NULL;
@@ -822,11 +834,13 @@
 	bprm.loader = 0;
 	bprm.exec = 0;
 	if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {
+		allow_write_access(file);
 		fput(file);
 		return bprm.argc;
 	}
 
 	if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {
+		allow_write_access(file);
 		fput(file);
 		return bprm.envc;
 	}
@@ -855,6 +869,7 @@
 
 out:
 	/* Something went wrong, return the inode and free the argument pages*/
+	allow_write_access(bprm.file);
 	if (bprm.file)
 		fput(bprm.file);
 
@@ -915,8 +930,8 @@
 		goto close_fail;
 	if (!binfmt->core_dump(signr, regs, file))
 		goto close_fail;
-	filp_close(file, NULL);
 	unlock_kernel();
+	filp_close(file, NULL);
 	return 1;
 
 close_fail:

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)