]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/open.c
fs/binfmt_elf.c: coredump: allocate core ELF header on stack
[linux.git] / fs / open.c
index b62f5c0923a80cc168904b940e564fb2721a4f40..0788b3715731186f326a24296ed3b00ad870bb46 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -955,48 +955,83 @@ struct file *open_with_fake_path(const struct path *path, int flags,
 }
 EXPORT_SYMBOL(open_with_fake_path);
 
-static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
+#define WILL_CREATE(flags)     (flags & (O_CREAT | __O_TMPFILE))
+#define O_PATH_FLAGS           (O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC)
+
+inline struct open_how build_open_how(int flags, umode_t mode)
+{
+       struct open_how how = {
+               .flags = flags & VALID_OPEN_FLAGS,
+               .mode = mode & S_IALLUGO,
+       };
+
+       /* O_PATH beats everything else. */
+       if (how.flags & O_PATH)
+               how.flags &= O_PATH_FLAGS;
+       /* Modes should only be set for create-like flags. */
+       if (!WILL_CREATE(how.flags))
+               how.mode = 0;
+       return how;
+}
+
+inline int build_open_flags(const struct open_how *how, struct open_flags *op)
 {
+       int flags = how->flags;
        int lookup_flags = 0;
        int acc_mode = ACC_MODE(flags);
 
+       /* Must never be set by userspace */
+       flags &= ~(FMODE_NONOTIFY | O_CLOEXEC);
+
        /*
-        * Clear out all open flags we don't know about so that we don't report
-        * them in fcntl(F_GETFD) or similar interfaces.
+        * Older syscalls implicitly clear all of the invalid flags or argument
+        * values before calling build_open_flags(), but openat2(2) checks all
+        * of its arguments.
         */
-       flags &= VALID_OPEN_FLAGS;
+       if (flags & ~VALID_OPEN_FLAGS)
+               return -EINVAL;
+       if (how->resolve & ~VALID_RESOLVE_FLAGS)
+               return -EINVAL;
 
-       if (flags & (O_CREAT | __O_TMPFILE))
-               op->mode = (mode & S_IALLUGO) | S_IFREG;
-       else
+       /* Deal with the mode. */
+       if (WILL_CREATE(flags)) {
+               if (how->mode & ~S_IALLUGO)
+                       return -EINVAL;
+               op->mode = how->mode | S_IFREG;
+       } else {
+               if (how->mode != 0)
+                       return -EINVAL;
                op->mode = 0;
-
-       /* Must never be set by userspace */
-       flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC;
+       }
 
        /*
-        * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
-        * check for O_DSYNC if the need any syncing at all we enforce it's
-        * always set instead of having to deal with possibly weird behaviour
-        * for malicious applications setting only __O_SYNC.
+        * In order to ensure programs get explicit errors when trying to use
+        * O_TMPFILE on old kernels, O_TMPFILE is implemented such that it
+        * looks like (O_DIRECTORY|O_RDWR & ~O_CREAT) to old kernels. But we
+        * have to require userspace to explicitly set it.
         */
-       if (flags & __O_SYNC)
-               flags |= O_DSYNC;
-
        if (flags & __O_TMPFILE) {
                if ((flags & O_TMPFILE_MASK) != O_TMPFILE)
                        return -EINVAL;
                if (!(acc_mode & MAY_WRITE))
                        return -EINVAL;
-       } else if (flags & O_PATH) {
-               /*
-                * If we have O_PATH in the open flag. Then we
-                * cannot have anything other than the below set of flags
-                */
-               flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
+       }
+       if (flags & O_PATH) {
+               /* O_PATH only permits certain other flags to be set. */
+               if (flags & ~O_PATH_FLAGS)
+                       return -EINVAL;
                acc_mode = 0;
        }
 
+       /*
+        * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
+        * check for O_DSYNC if the need any syncing at all we enforce it's
+        * always set instead of having to deal with possibly weird behaviour
+        * for malicious applications setting only __O_SYNC.
+        */
+       if (flags & __O_SYNC)
+               flags |= O_DSYNC;
+
        op->open_flag = flags;
 
        /* O_TRUNC implies we need access checks for write permissions */
@@ -1022,6 +1057,18 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
                lookup_flags |= LOOKUP_DIRECTORY;
        if (!(flags & O_NOFOLLOW))
                lookup_flags |= LOOKUP_FOLLOW;
+
+       if (how->resolve & RESOLVE_NO_XDEV)
+               lookup_flags |= LOOKUP_NO_XDEV;
+       if (how->resolve & RESOLVE_NO_MAGICLINKS)
+               lookup_flags |= LOOKUP_NO_MAGICLINKS;
+       if (how->resolve & RESOLVE_NO_SYMLINKS)
+               lookup_flags |= LOOKUP_NO_SYMLINKS;
+       if (how->resolve & RESOLVE_BENEATH)
+               lookup_flags |= LOOKUP_BENEATH;
+       if (how->resolve & RESOLVE_IN_ROOT)
+               lookup_flags |= LOOKUP_IN_ROOT;
+
        op->lookup_flags = lookup_flags;
        return 0;
 }
@@ -1040,8 +1087,11 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
 struct file *file_open_name(struct filename *name, int flags, umode_t mode)
 {
        struct open_flags op;
-       int err = build_open_flags(flags, mode, &op);
-       return err ? ERR_PTR(err) : do_filp_open(AT_FDCWD, name, &op);
+       struct open_how how = build_open_how(flags, mode);
+       int err = build_open_flags(&how, &op);
+       if (err)
+               return ERR_PTR(err);
+       return do_filp_open(AT_FDCWD, name, &op);
 }
 
 /**
@@ -1072,17 +1122,19 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
                            const char *filename, int flags, umode_t mode)
 {
        struct open_flags op;
-       int err = build_open_flags(flags, mode, &op);
+       struct open_how how = build_open_how(flags, mode);
+       int err = build_open_flags(&how, &op);
        if (err)
                return ERR_PTR(err);
        return do_file_open_root(dentry, mnt, filename, &op);
 }
 EXPORT_SYMBOL(file_open_root);
 
-long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
+static long do_sys_openat2(int dfd, const char __user *filename,
+                          struct open_how *how)
 {
        struct open_flags op;
-       int fd = build_open_flags(flags, mode, &op);
+       int fd = build_open_flags(how, &op);
        struct filename *tmp;
 
        if (fd)
@@ -1092,7 +1144,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
        if (IS_ERR(tmp))
                return PTR_ERR(tmp);
 
-       fd = get_unused_fd_flags(flags);
+       fd = get_unused_fd_flags(how->flags);
        if (fd >= 0) {
                struct file *f = do_filp_open(dfd, tmp, &op);
                if (IS_ERR(f)) {
@@ -1107,12 +1159,16 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
        return fd;
 }
 
-SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
+long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
 {
-       if (force_o_largefile())
-               flags |= O_LARGEFILE;
+       struct open_how how = build_open_how(flags, mode);
+       return do_sys_openat2(dfd, filename, &how);
+}
 
-       return do_sys_open(AT_FDCWD, filename, flags, mode);
+
+SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
+{
+       return ksys_open(filename, flags, mode);
 }
 
 SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
@@ -1120,10 +1176,32 @@ SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
 {
        if (force_o_largefile())
                flags |= O_LARGEFILE;
-
        return do_sys_open(dfd, filename, flags, mode);
 }
 
+SYSCALL_DEFINE4(openat2, int, dfd, const char __user *, filename,
+               struct open_how __user *, how, size_t, usize)
+{
+       int err;
+       struct open_how tmp;
+
+       BUILD_BUG_ON(sizeof(struct open_how) < OPEN_HOW_SIZE_VER0);
+       BUILD_BUG_ON(sizeof(struct open_how) != OPEN_HOW_SIZE_LATEST);
+
+       if (unlikely(usize < OPEN_HOW_SIZE_VER0))
+               return -EINVAL;
+
+       err = copy_struct_from_user(&tmp, sizeof(tmp), how, usize);
+       if (err)
+               return err;
+
+       /* O_LARGEFILE is only allowed for non-O_PATH. */
+       if (!(tmp.flags & O_PATH) && force_o_largefile())
+               tmp.flags |= O_LARGEFILE;
+
+       return do_sys_openat2(dfd, filename, &tmp);
+}
+
 #ifdef CONFIG_COMPAT
 /*
  * Exactly like sys_open(), except that it doesn't set the