diff --git a/lib/libc/sys/execve.2 b/lib/libc/sys/execve.2
index 2da72902f91..ba1685c5076 100644
--- a/lib/libc/sys/execve.2
+++ b/lib/libc/sys/execve.2
@@ -60,7 +60,9 @@ descriptor
 .Fa fd
 instead of a
 .Fa path .
-This file is either an executable object file,
+This
+.Em new process file
+is either an executable object file,
 or a file of data for an interpreter.
 An executable object file consists of an identifying header,
 followed by pages of data representing the initial program (text)
@@ -131,6 +133,13 @@ and
 .Xr fcntl 2 ) .
 Descriptors that remain open are unaffected by
 .Fn execve .
+If any of the standard descriptors (0, 1, and/or 2) are closed at the time
+.Fn execve
+is called, and the process will gain privilege as a result of set-id
+semantics, those descriptors will be reopened automatically.
+No programs, whether privileged or not, should assume that these descriptors
+will remain closed across a call to
+.Fn execve .
 .Pp
 Signals set to be ignored in the calling process are set to be ignored in
 the
@@ -306,7 +315,9 @@ If a program is
 to a non-super-user, but is executed when
 the real
 .Em uid
-is ``root'', then the program has some of the powers
+is
+.Dq root ,
+then the program has some of the powers
 of a super-user as well.
 .Sh SEE ALSO
 .Xr ktrace 1 ,
@@ -320,11 +331,23 @@ of a super-user as well.
 .Xr environ 7 ,
 .Xr script 7 ,
 .Xr mount 8
+.Sh STANDARDS
+The
+.Fn execve
+system call conforms to
+.St -p1003.1-2004 .
+The
+.Fn fexecve
+system call conforms to
+.St -p1003.1-2008 .
+.Pp
+The support for executing interpreted programs is an extension.
 .Sh HISTORY
 The
 .Fn execve
 function call appeared in
-.Bx 4.2 .
+.At v7 .
 The
 .Fn fexecve
-system call conforms to The Open Group Extended API Set 2 specification.
+function first appeared in
+.Dx 5.9 .
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 254a81d0e1b..a692d369b78 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -660,44 +660,71 @@ sys_execve(struct sysmsg *sysmsg, const struct execve_args *uap)
 int
 sys_fexecve(struct sysmsg *sysmsg, const struct fexecve_args *uap)
 {
-	struct file *fp;
-	struct image_args args;
 	struct nlookupdata nd;
+	struct image_args args;
 	struct thread *td = curthread;
 	struct vnode *vp;
-	char *freepath, *fullpath;
+	struct file *fp;
+	char *fullpath, *freepath;
+	char fname[32];
 	int error;
 
-	error = holdvnode(td, uap->fd, &fp);
-	if (error != 0)
+	fullpath = freepath = NULL;
+	bzero(&args, sizeof(args));
+
+	if ((error = holdvnode(td, uap->fd, &fp)) != 0)
+		return (error);
+
+	/*
+	 * Only allow descriptor opened only with O_RDONLY or O_EXEC
+	 * XXX: missing O_EXEC support
+	 */
+	if ((fp->f_flag & FWRITE) != 0 || (fp->f_flag & FREAD) == 0) {
+		error = EBADF;
 		goto done;
+	}
+
 	vp = fp->f_data;
-	fdrop(fp);
-	freepath = NULL;
-	fullpath = "-";
 	error = vn_fullpath(td->td_proc, vp, &fullpath, &freepath, 0);
-	if (error != 0)
+	if (error)
 		goto done;
-	bzero(&args, sizeof(args));
+
 	error = nlookup_init(&nd, fullpath, UIO_SYSSPACE, NLC_FOLLOW);
 	if (error == 0) {
-		error = exec_copyin_args(&args, NULL, PATH_SYSSPACE, uap->argv,
-		    uap->envv);
+		/*
+		 * exec_copyin_args() requires the 'fname' argument,
+		 * which is needed in executing an interpreted program
+		 * because the interpreter must know the script path.
+		 * Supply it with '/dev/fd/n'.
+		 */
+		ksnprintf(fname, sizeof(fname), "/dev/fd/%d", uap->fd);
+		error = exec_copyin_args(&args, fname, PATH_SYSSPACE,
+					 uap->argv, uap->envv);
 	}
-	if (freepath)
-		kfree(freepath, M_TEMP);
 	if (error == 0)
 		error = kern_execve(&nd, &args);
+
 	nlookup_done(&nd);
 	exec_free_args(&args);
+
 	if (error < 0) {
 		/* We hit a lethal error condition.  Let's die now. */
 		exit1(W_EXITCODE(0, SIGABRT));
 		/* NOTREACHED */
 	}
+
+	/*
+	 * The syscall result is returned in registers to the new program.
+	 * Linux will register %edx as an atexit function and we must be
+	 * sure to set it to 0.  XXX
+	 */
 	if (error == 0)
 		sysmsg->sysmsg_result64 = 0;
+
 done:
+	if (freepath != NULL)
+		kfree(freepath, M_TEMP);
+	fdrop(fp);
 	return (error);
 }
 
@@ -957,24 +984,25 @@ exec_copyin_args(struct image_args *args, char *fname,
 	args->buf = objcache_get(exec_objcache, M_WAITOK);
 	if (args->buf == NULL)
 		return (ENOMEM);
+
 	args->begin_argv = args->buf;
 	args->endp = args->begin_argv;
 	args->space = ARG_MAX;
 
 	args->fname = args->buf + ARG_MAX;
 
-	if (fname != NULL) {
-		/*
-		 * Copy the file name.
-		 */
-		if (segflg == PATH_SYSSPACE) {
-			error = copystr(fname, args->fname, PATH_MAX, &length);
-		} else if (segflg == PATH_USERSPACE) {
-			error = copyinstr(fname, args->fname, PATH_MAX, &length);
-		}
+	/*
+	 * Copy the file name.
+	 */
+	if (segflg == PATH_SYSSPACE) {
+		error = copystr(fname, args->fname,
+				PATH_MAX, &length);
 	} else {
-		length = 0;
+		error = copyinstr(fname, args->fname,
+				  PATH_MAX, &length);
 	}
+	if (error)
+		return (error);
 
 	/*
 	 * Extract argument strings.  argv may not be NULL.  The argv
@@ -1044,6 +1072,7 @@ exec_copyin_args(struct image_args *args, char *fname,
 			args->envc++;
 		}
 	}
+
 	return (error);
 }
 
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 4d1ff905acf..c5e2c2056a5 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -224,9 +224,10 @@
  * 500906 - add struct ip_mreqn for IPPROTO_IP/IP_MULTICAST_IF,
  *          IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
  * 500907 - add clock_nanosleep()
+ * 500908 - add fexecve()
  */
 #undef __DragonFly_version
-#define __DragonFly_version 500907	/* propagated to newvers */
+#define __DragonFly_version 500908	/* propagated to newvers */
 
 #include <sys/_null.h>
 
