@@ -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;
}
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@redhat.com> --- fs/open.c | 53 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 18 deletions(-)