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 <mguzik@xxxxxxxxxx> --- 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-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html