kernel: Fix error for dup2 if the new file descriptor is out of bounds.
authorSascha Wildner <saw@online.de>
Thu, 21 Aug 2014 20:12:01 +0000 (22:12 +0200)
committerSascha Wildner <saw@online.de>
Thu, 21 Aug 2014 20:12:01 +0000 (22:12 +0200)
According to POSIX, when using fcntl()'s F_DUPFD or F_DUPFD_CLOEXEC
commands and the new file descriptor is out of bounds, EINVAL must be
returned. But if it is out of bounds when using dup2(), EBADF must be
returned.

So add detection of whether kern_dup() was called from kern_fcntl() or
from sys_dup2() and act accordingly.

sys/kern/kern_descrip.c
sys/sys/kern_syscall.h

index 5925185..2b7b2b2 100644 (file)
@@ -251,16 +251,17 @@ kern_fcntl(int fd, int cmd, union fcntl_dat *dat, struct ucred *cred)
                return (error);
        case F_DUPFD:
                newmin = dat->fc_fd;
-               error = kern_dup(DUP_VARIABLE, fd, newmin, &dat->fc_fd);
+               error = kern_dup(DUP_VARIABLE | DUP_FCNTL, fd, newmin,
+                   &dat->fc_fd);
                return (error);
-       case F_DUP2FD:
+       case F_DUPFD_CLOEXEC:
                newmin = dat->fc_fd;
-               error = kern_dup(DUP_FIXED, fd, newmin, &dat->fc_fd);
+               error = kern_dup(DUP_VARIABLE | DUP_CLOEXEC | DUP_FCNTL,
+                   fd, newmin, &dat->fc_fd);
                return (error);
-       case F_DUPFD_CLOEXEC:
+       case F_DUP2FD:
                newmin = dat->fc_fd;
-               error = kern_dup(DUP_VARIABLE | DUP_CLOEXEC, fd, newmin,
-                                &dat->fc_fd);
+               error = kern_dup(DUP_FIXED, fd, newmin, &dat->fc_fd);
                return (error);
        case F_DUP2FD_CLOEXEC:
                newmin = dat->fc_fd;
@@ -476,8 +477,12 @@ sys_fcntl(struct fcntl_args *uap)
 /*
  * Common code for dup, dup2, and fcntl(F_DUPFD).
  *
- * There are three type flags: DUP_FIXED, DUP_VARIABLE, and DUP_CLOEXEC.
- * The first two flags are mutually exclusive, and the third is optional.
+ * There are four type flags: DUP_FCNTL, DUP_FIXED, DUP_VARIABLE, and
+ * DUP_CLOEXEC.
+ *
+ * DUP_FCNTL is for handling EINVAL vs. EBADF differences between
+ * fcntl()'s F_DUPFD and F_DUPFD_CLOEXEC and dup()/dup2 (per POSIX).
+ * The next two flags are mutually exclusive, and the fourth is optional.
  * DUP_FIXED tells kern_dup() to destructively dup over an existing file
  * descriptor if "new" is already open.  DUP_VARIABLE tells kern_dup()
  * to find the lowest unused file descriptor that is greater than or
@@ -501,7 +506,10 @@ kern_dup(int flags, int old, int new, int *res)
 
        /*
         * Verify that we have a valid descriptor to dup from and
-        * possibly to dup to.
+        * possibly to dup to. When the new descriptor is out of
+        * bounds, fcntl()'s F_DUPFD and F_DUPFD_CLOEXEC must
+        * return EINVAL, while dup() and dup2() return EBADF in
+        * this case.
         *
         * NOTE: maxfilesperuser is not applicable to dup()
         */
@@ -516,7 +524,7 @@ retry:
                dtsize = minfilesperproc;
 
        if (new < 0 || new > dtsize)
-               return (EINVAL);
+               return (flags & DUP_FCNTL ? EINVAL : EBADF);
 
        spin_lock(&fdp->fd_spin);
        if ((unsigned)old >= fdp->fd_nfiles || fdp->fd_files[old].fp == NULL) {
index bd49dbc..4da574b 100644 (file)
@@ -24,8 +24,6 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
- * $DragonFly: src/sys/sys/kern_syscall.h,v 1.38 2008/06/01 19:27:37 dillon Exp $
  */
 
 #ifndef _SYS_KERN_SYSCALL_H_
@@ -40,6 +38,7 @@
 #define DUP_FIXED      0x1     /* Copy to specific fd even if in use */
 #define DUP_VARIABLE   0x2     /* Copy fd to an unused fd */
 #define DUP_CLOEXEC    0x4     /* Set fd close on exec flag */
+#define DUP_FCNTL      0x8     /* Set for F_DUPFD and F_DUPFD_CLOEXEC */
 union fcntl_dat;
 struct image_args;
 struct plimit;