Sometimes faccessat needs to modify current thread's credentials, but
calls prepare_creds unconditionally.

Take advantage of the fact that we can detect whether any modification
to credentials is needed and in turn avoid unnecessary allocations.

Signed-off-by: Mateusz Guzik <mgu...@redhat.com>
---
 fs/open.c | 53 +++++++++++++++++++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 18 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index 33f9cbf..166eb45 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -330,8 +330,10 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, 
offset, loff_t, len)
  */
 SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
 {
-       const struct cred *old_cred;
-       struct cred *override_cred;
+       const struct cred *old_cred = current_cred();
+       struct cred *override_cred = NULL;
+       kernel_cap_t cap_effective;
+       int modify_cap_effective = 0;
        struct path path;
        struct inode *inode;
        int res;
@@ -340,24 +342,37 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, 
filename, int, mode)
        if (mode & ~S_IRWXO)    /* where's F_OK, X_OK, W_OK, R_OK? */
                return -EINVAL;
 
-       override_cred = prepare_creds();
-       if (!override_cred)
-               return -ENOMEM;
-
-       override_cred->fsuid = override_cred->uid;
-       override_cred->fsgid = override_cred->gid;
-
        if (!issecure(SECURE_NO_SETUID_FIXUP)) {
                /* Clear the capabilities if we switch to a non-root user */
-               kuid_t root_uid = make_kuid(override_cred->user_ns, 0);
-               if (!uid_eq(override_cred->uid, root_uid))
-                       cap_clear(override_cred->cap_effective);
-               else
-                       override_cred->cap_effective =
-                               override_cred->cap_permitted;
+               kuid_t root_uid = make_kuid(old_cred->user_ns, 0);
+               if (!uid_eq(old_cred->uid, root_uid)) {
+                       if (!cap_isclear(old_cred->cap_effective)) {
+                               cap_clear(cap_effective);
+                               modify_cap_effective = 1;
+                       }
+               } else {
+                       if (!cap_isequal(old_cred->cap_effective,
+                               old_cred->cap_permitted)) {
+                               cap_effective = old_cred->cap_permitted;
+                               modify_cap_effective = 1;
+                       }
+               }
        }
 
-       old_cred = override_creds(override_cred);
+       if (!uid_eq(old_cred->fsuid, old_cred->uid) ||
+           !gid_eq(old_cred->fsgid, old_cred->gid) ||
+           modify_cap_effective) {
+               override_cred = prepare_creds();
+               if (!override_cred)
+                       return -ENOMEM;
+
+               override_cred->fsuid = override_cred->uid;
+               override_cred->fsgid = override_cred->gid;
+               if (modify_cap_effective)
+                       override_cred->cap_effective = cap_effective;
+
+               override_creds(override_cred);
+       }
 retry:
        res = user_path_at(dfd, filename, lookup_flags, &path);
        if (res)
@@ -399,8 +414,10 @@ out_path_release:
                goto retry;
        }
 out:
-       revert_creds(old_cred);
-       put_cred(override_cred);
+       if (override_cred) {
+               revert_creds(old_cred);
+               put_cred(override_cred);
+       }
        return res;
 }
 
-- 
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to