They are 2 related patches for setfsgid().
Patch 1 for bug fix: return the current gid when error occurs.
Patch 2 for cleaning code: remove useless variable 'old_fsgid'.
Signed-off-by: Chen Gang <[email protected]>
--
kernel/sys.c | 15 +++++----------
1 files changed, 5 insertions(+), 10 deletions(-)
According to the API definition, when error occurs, need return current
fsgid instead of the previous one.
The related informations ("man setfsgid"):
RETURN VALUE
On success, the previous value of fsgid is returned. On error, the current value of fsgid is returned.
Signed-off-by: Chen Gang <[email protected]>
---
kernel/sys.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/kernel/sys.c b/kernel/sys.c
index 771129b..9356dc8 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -775,11 +775,11 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
kgid = make_kgid(old->user_ns, gid);
if (!gid_valid(kgid))
- return old_fsgid;
+ return gid;
new = prepare_creds();
if (!new)
- return old_fsgid;
+ return gid;
if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->egid) ||
gid_eq(kgid, old->sgid) || gid_eq(kgid, old->fsgid) ||
@@ -791,7 +791,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
}
abort_creds(new);
- return old_fsgid;
+ return gid;
change_okay:
commit_creds(new);
--
1.7.7.6
Remove useless variable 'old_fsgid' to make code clearer for readers.
Signed-off-by: Chen Gang <[email protected]>
---
kernel/sys.c | 9 ++-------
1 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/kernel/sys.c b/kernel/sys.c
index 9356dc8..fc169d8 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -767,11 +767,9 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
{
const struct cred *old;
struct cred *new;
- gid_t old_fsgid;
kgid_t kgid;
old = current_cred();
- old_fsgid = from_kgid_munged(old->user_ns, old->fsgid);
kgid = make_kgid(old->user_ns, gid);
if (!gid_valid(kgid))
@@ -786,16 +784,13 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
nsown_capable(CAP_SETGID)) {
if (!gid_eq(kgid, old->fsgid)) {
new->fsgid = kgid;
- goto change_okay;
+ commit_creds(new);
+ return from_kgid_munged(old->user_ns, old->fsgid);
}
}
abort_creds(new);
return gid;
-
-change_okay:
- commit_creds(new);
- return old_fsgid;
}
/**
--
1.7.7.6
They are 2 related patches for setfsuid().
Patch 1 for bug fix: return the current uid when error occurs.
Patch 2 for cleaning code: remove useless variable 'old_fsuid'.
Signed-off-by: Chen Gang <[email protected]>
--
kernel/sys.c | 14 ++++++--------
1 files changed, 6 insertions(+), 8 deletions(-)
Please skip it, sorry for bothering you with waste information.
On 08/06/2013 04:13 PM, Chen Gang wrote:
> They are 2 related patches for setfsuid().
>
> Patch 1 for bug fix: return the current uid when error occurs.
> Patch 2 for cleaning code: remove useless variable 'old_fsuid'.
>
> Signed-off-by: Chen Gang <[email protected]>
> --
> kernel/sys.c | 14 ++++++--------
> 1 files changed, 6 insertions(+), 8 deletions(-)
>
--
Chen Gang
They are 2 related patches for setfsuid().
Patch 1 for bug fix: return the current uid when error occurs.
Patch 2 for cleaning code: remove useless variable 'old_fsuid'.
Signed-off-by: Chen Gang <[email protected]>
--
kernel/sys.c | 14 ++++++--------
1 files changed, 6 insertions(+), 8 deletions(-)
According to the API definition, when error occurs, need return current
fsuid instead of the previous one.
The related informations ("man setfsuid"):
RETURN VALUE
On success, the previous value of fsuid is returned. On error, the current value of fsuid is returned.
Signed-off-by: Chen Gang <[email protected]>
---
kernel/sys.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/kernel/sys.c b/kernel/sys.c
index 771129b..558ccdb 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -736,11 +736,11 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
kuid = make_kuid(old->user_ns, uid);
if (!uid_valid(kuid))
- return old_fsuid;
+ return uid;
new = prepare_creds();
if (!new)
- return old_fsuid;
+ return uid;
if (uid_eq(kuid, old->uid) || uid_eq(kuid, old->euid) ||
uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) ||
@@ -753,7 +753,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
}
abort_creds(new);
- return old_fsuid;
+ return uid;
change_okay:
commit_creds(new);
--
1.7.7.6
Remove useless variable 'old_fsuid' to make code clearer for readers.
Signed-off-by: Chen Gang <[email protected]>
---
kernel/sys.c | 14 ++++++--------
1 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/kernel/sys.c b/kernel/sys.c
index 558ccdb..e3983b2 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -728,11 +728,9 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
{
const struct cred *old;
struct cred *new;
- uid_t old_fsuid;
kuid_t kuid;
old = current_cred();
- old_fsuid = from_kuid_munged(old->user_ns, old->fsuid);
kuid = make_kuid(old->user_ns, uid);
if (!uid_valid(kuid))
@@ -747,17 +745,17 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
nsown_capable(CAP_SETUID)) {
if (!uid_eq(kuid, old->fsuid)) {
new->fsuid = kuid;
- if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
- goto change_okay;
+ if (security_task_fix_setuid(new,
+ old, LSM_SETID_FS) == 0) {
+ commit_creds(new);
+ return from_kuid_munged(old->user_ns,
+ old->fsuid);
+ }
}
}
abort_creds(new);
return uid;
-
-change_okay:
- commit_creds(new);
- return old_fsuid;
}
/*
--
1.7.7.6
Improve the usage of return value, so not only can make code clearer
to readers, but also can improve the performance.
Assign the return error code only when error occurs, so can save some
instructions.
For getcpu(), uname(), newuname(), and override_release(), can remove
the return variable to make code clearer and save some instructions.
Also modify the related code to pass "./scripts/checkpatch.pl" checking.
Signed-off-by: Chen Gang <[email protected]>
---
kernel/sys.c | 147 +++++++++++++++++++++++++++++++---------------------------
1 files changed, 79 insertions(+), 68 deletions(-)
diff --git a/kernel/sys.c b/kernel/sys.c
index 771129b..a63a350 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -386,13 +386,14 @@ SYSCALL_DEFINE1(setgid, gid_t, gid)
return -ENOMEM;
old = current_cred();
- retval = -EPERM;
if (nsown_capable(CAP_SETGID))
new->gid = new->egid = new->sgid = new->fsgid = kgid;
else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid))
new->egid = new->fsgid = kgid;
- else
+ else {
+ retval = -EPERM;
goto error;
+ }
return commit_creds(new);
@@ -533,7 +534,6 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)
return -ENOMEM;
old = current_cred();
- retval = -EPERM;
if (nsown_capable(CAP_SETUID)) {
new->suid = new->uid = kuid;
if (!uid_eq(kuid, old->uid)) {
@@ -542,6 +542,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)
goto error;
}
} else if (!uid_eq(kuid, old->uid) && !uid_eq(kuid, new->suid)) {
+ retval = -EPERM;
goto error;
}
@@ -919,31 +920,35 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
*/
write_lock_irq(&tasklist_lock);
- err = -ESRCH;
p = find_task_by_vpid(pid);
- if (!p)
+ if (!p) {
+ err = -ESRCH;
goto out;
+ }
- err = -EINVAL;
- if (!thread_group_leader(p))
+ if (!thread_group_leader(p)) {
+ err = -EINVAL;
goto out;
+ }
if (same_thread_group(p->real_parent, group_leader)) {
- err = -EPERM;
- if (task_session(p) != task_session(group_leader))
+ if (task_session(p) != task_session(group_leader)) {
+ err = -EPERM;
goto out;
- err = -EACCES;
- if (p->did_exec)
+ }
+ if (p->did_exec) {
+ err = -EACCES;
goto out;
- } else {
+ }
+ } else if (p != group_leader) {
err = -ESRCH;
- if (p != group_leader)
- goto out;
+ goto out;
}
- err = -EPERM;
- if (p->signal->leader)
+ if (p->signal->leader) {
+ err = -EPERM;
goto out;
+ }
pgrp = task_pid(p);
if (pgid != pid) {
@@ -951,8 +956,10 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
pgrp = find_vpid(pgid);
g = pid_task(pgrp, PIDTYPE_PGID);
- if (!g || task_session(g) != task_session(group_leader))
+ if (!g || task_session(g) != task_session(group_leader)) {
+ err = -EPERM;
goto out;
+ }
}
err = security_task_setpgid(p, pgid);
@@ -962,7 +969,6 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
if (task_pgrp(p) != pgrp)
change_pid(p, PIDTYPE_PGID, pgrp);
- err = 0;
out:
/* All paths lead to here, thus we are safe. -DaveM */
write_unlock_irq(&tasklist_lock);
@@ -980,14 +986,16 @@ SYSCALL_DEFINE1(getpgid, pid_t, pid)
if (!pid)
grp = task_pgrp(current);
else {
- retval = -ESRCH;
p = find_task_by_vpid(pid);
- if (!p)
+ if (!p) {
+ retval = -ESRCH;
goto out;
+ }
grp = task_pgrp(p);
- if (!grp)
+ if (!grp) {
+ retval = -ESRCH;
goto out;
-
+ }
retval = security_task_getpgid(p);
if (retval)
goto out;
@@ -1017,14 +1025,16 @@ SYSCALL_DEFINE1(getsid, pid_t, pid)
if (!pid)
sid = task_session(current);
else {
- retval = -ESRCH;
p = find_task_by_vpid(pid);
- if (!p)
+ if (!p) {
+ retval = -ESRCH;
goto out;
+ }
sid = task_session(p);
- if (!sid)
+ if (!sid) {
+ retval = -ESRCH;
goto out;
-
+ }
retval = security_task_getsid(p);
if (retval)
goto out;
@@ -1096,8 +1106,6 @@ DECLARE_RWSEM(uts_sem);
*/
static int override_release(char __user *release, size_t len)
{
- int ret = 0;
-
if (current->personality & UNAME26) {
const char *rest = UTS_RELEASE;
char buf[65] = { 0 };
@@ -1115,25 +1123,25 @@ static int override_release(char __user *release, size_t len)
v = ((LINUX_VERSION_CODE >> 8) & 0xff) + 40;
copy = clamp_t(size_t, len, 1, sizeof(buf));
copy = scnprintf(buf, copy, "2.6.%u%s", v, rest);
- ret = copy_to_user(release, buf, copy + 1);
+ return copy_to_user(release, buf, copy + 1);
}
- return ret;
+ return 0;
}
SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
{
- int errno = 0;
-
down_read(&uts_sem);
- if (copy_to_user(name, utsname(), sizeof *name))
- errno = -EFAULT;
+ if (copy_to_user(name, utsname(), sizeof(*name))) {
+ up_read(&uts_sem);
+ return -EFAULT;
+ }
up_read(&uts_sem);
- if (!errno && override_release(name->release, sizeof(name->release)))
- errno = -EFAULT;
- if (!errno && override_architecture(name))
- errno = -EFAULT;
- return errno;
+ if (override_release(name->release, sizeof(name->release)))
+ return -EFAULT;
+ if (override_architecture(name))
+ return -EFAULT;
+ return 0;
}
#ifdef __ARCH_WANT_SYS_OLD_UNAME
@@ -1142,21 +1150,21 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
*/
SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
{
- int error = 0;
-
if (!name)
return -EFAULT;
down_read(&uts_sem);
- if (copy_to_user(name, utsname(), sizeof(*name)))
- error = -EFAULT;
+ if (copy_to_user(name, utsname(), sizeof(*name))) {
+ up_read(&uts_sem);
+ return -EFAULT;
+ }
up_read(&uts_sem);
- if (!error && override_release(name->release, sizeof(name->release)))
- error = -EFAULT;
- if (!error && override_architecture(name))
- error = -EFAULT;
- return error;
+ if (override_release(name->release, sizeof(name->release)))
+ return -EFAULT;
+ if (override_architecture(name))
+ return -EFAULT;
+ return 0;
}
SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
@@ -1185,12 +1193,14 @@ SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
__OLD_UTS_LEN);
error |= __put_user(0, name->machine + __OLD_UTS_LEN);
up_read(&uts_sem);
+ if (error)
+ return -EFAULT;
- if (!error && override_architecture(name))
- error = -EFAULT;
- if (!error && override_release(name->release, sizeof(name->release)))
- error = -EFAULT;
- return error ? -EFAULT : 0;
+ if (override_architecture(name))
+ return -EFAULT;
+ if (override_release(name->release, sizeof(name->release)))
+ return -EFAULT;
+ return 0;
}
#endif
@@ -1648,10 +1658,11 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
* sure that this one is executable as well, to avoid breaking an
* overall picture.
*/
- err = -EACCES;
- if (!S_ISREG(inode->i_mode) ||
- exe.file->f_path.mnt->mnt_flags & MNT_NOEXEC)
+ if (!S_ISREG(inode->i_mode) ||
+ exe.file->f_path.mnt->mnt_flags & MNT_NOEXEC) {
+ err = -EACCES;
goto exit;
+ }
err = inode_permission(inode, MAY_EXEC);
if (err)
@@ -1662,15 +1673,16 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
/*
* Forbid mm->exe_file change if old file still mapped.
*/
- err = -EBUSY;
if (mm->exe_file) {
struct vm_area_struct *vma;
for (vma = mm->mmap; vma; vma = vma->vm_next)
if (vma->vm_file &&
path_equal(&vma->vm_file->f_path,
- &mm->exe_file->f_path))
+ &mm->exe_file->f_path)) {
+ err = -EBUSY;
goto exit_unlock;
+ }
}
/*
@@ -1679,11 +1691,10 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
* could make a snapshot over all processes running and monitor
* /proc/pid/exe changes to notice unusual activity if needed.
*/
- err = -EPERM;
- if (test_and_set_bit(MMF_EXE_FILE_CHANGED, &mm->flags))
+ if (test_and_set_bit(MMF_EXE_FILE_CHANGED, &mm->flags)) {
+ err = -EPERM;
goto exit_unlock;
-
- err = 0;
+ }
set_mm_exe_file(mm, exe.file); /* this grabs a reference to exe.file */
exit_unlock:
up_write(&mm->mmap_sem);
@@ -2009,13 +2020,13 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
struct getcpu_cache __user *, unused)
{
- int err = 0;
int cpu = raw_smp_processor_id();
- if (cpup)
- err |= put_user(cpu, cpup);
- if (nodep)
- err |= put_user(cpu_to_node(cpu), nodep);
- return err ? -EFAULT : 0;
+
+ if (cpup && put_user(cpu, cpup))
+ return -EFAULT;
+ if (nodep && put_user(cpu_to_node(cpu), nodep))
+ return -EFAULT;
+ return 0;
}
/**
--
1.7.7.6
On 08/06/2013 04:16 PM, Chen Gang wrote:
> Remove useless variable 'old_fsuid' to make code clearer for readers.
>
> Signed-off-by: Chen Gang <[email protected]>
> ---
> kernel/sys.c | 14 ++++++--------
> 1 files changed, 6 insertions(+), 8 deletions(-)
>
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 558ccdb..e3983b2 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -728,11 +728,9 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
> {
> const struct cred *old;
> struct cred *new;
> - uid_t old_fsuid;
> kuid_t kuid;
>
> old = current_cred();
> - old_fsuid = from_kuid_munged(old->user_ns, old->fsuid);
>
Oh... should old_fsuid be get before commit_creds() or prepare_creds() ?
> kuid = make_kuid(old->user_ns, uid);
> if (!uid_valid(kuid))
> @@ -747,17 +745,17 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
> nsown_capable(CAP_SETUID)) {
> if (!uid_eq(kuid, old->fsuid)) {
> new->fsuid = kuid;
> - if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
> - goto change_okay;
> + if (security_task_fix_setuid(new,
> + old, LSM_SETID_FS) == 0) {
> + commit_creds(new);
> + return from_kuid_munged(old->user_ns,
> + old->fsuid);
> + }
Oh... should old_fsuid be get before commit_creds() or prepare_creds() ?
> }
> }
>
> abort_creds(new);
> return uid;
> -
> -change_okay:
> - commit_creds(new);
> - return old_fsuid;
> }
>
> /*
>
--
Chen Gang
On 08/06/2013 04:02 PM, Chen Gang wrote:
> Remove useless variable 'old_fsgid' to make code clearer for readers.
>
> Signed-off-by: Chen Gang <[email protected]>
> ---
> kernel/sys.c | 9 ++-------
> 1 files changed, 2 insertions(+), 7 deletions(-)
>
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 9356dc8..fc169d8 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -767,11 +767,9 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
> {
> const struct cred *old;
> struct cred *new;
> - gid_t old_fsgid;
> kgid_t kgid;
>
> old = current_cred();
> - old_fsgid = from_kgid_munged(old->user_ns, old->fsgid);
>
> kgid = make_kgid(old->user_ns, gid);
> if (!gid_valid(kgid))
> @@ -786,16 +784,13 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
> nsown_capable(CAP_SETGID)) {
> if (!gid_eq(kgid, old->fsgid)) {
> new->fsgid = kgid;
> - goto change_okay;
> + commit_creds(new);
> + return from_kgid_munged(old->user_ns, old->fsgid);
The same, should old_fsgid be get before commit_creds() or prepare_creds() ?
Thanks.
> }
> }
>
> abort_creds(new);
> return gid;
> -
> -change_okay:
> - commit_creds(new);
> - return old_fsgid;
> }
>
> /**
>
--
Chen Gang
On Tue, Aug 6, 2013 at 1:00 AM, Chen Gang <[email protected]> wrote:
> They are 2 related patches for setfsgid().
>
> Patch 1 for bug fix: return the current gid when error occurs.
> Patch 2 for cleaning code: remove useless variable 'old_fsgid'.
>
> Signed-off-by: Chen Gang <[email protected]>
> --
> kernel/sys.c | 15 +++++----------
> 1 files changed, 5 insertions(+), 10 deletions(-)
Making a change like this might have dramatic effects. So, a few
questions, to help better understand:
How long as the behavior been this way on Linux?
What is the origin of the documentation that states it differently?
Do existing userspace tools already depend on the current behavior?
What specific problem will be solved by changing this?
Thanks,
-Kees
--
Kees Cook
Chrome OS Security
On 08/06/2013 01:01 AM, Chen Gang wrote:
> According to the API definition, when error occurs, need return current
> fsgid instead of the previous one.
>
> The related informations ("man setfsgid"):
>
> RETURN VALUE
> On success, the previous value of fsgid is returned. On error, the current value of fsgid is returned.
>
> Signed-off-by: Chen Gang <[email protected]>
> ---
> kernel/sys.c | 6 +++---
> 1 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 771129b..9356dc8 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -775,11 +775,11 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
>
> kgid = make_kgid(old->user_ns, gid);
> if (!gid_valid(kgid))
> - return old_fsgid;
> + return gid;
Huh? So if 1234567 is not a valid gid, then setfsgid(1234567) is
supposed to return 1234567? This makes no sense.
I assume that what the man page means is that the return value is
whatever fsgid was prior to the call. On error, fsgid isn't changed, so
the return value is still "current".
(FWIW, this behavior is awful and is probably the cause of a security
bug or three, since success and failure are indistinguishable. But I
think your patch is wrong.)
--Andy
On 08/07/2013 02:36 AM, Kees Cook wrote:
> On Tue, Aug 6, 2013 at 1:00 AM, Chen Gang <[email protected]> wrote:
>> They are 2 related patches for setfsgid().
>>
>> Patch 1 for bug fix: return the current gid when error occurs.
>> Patch 2 for cleaning code: remove useless variable 'old_fsgid'.
>>
>> Signed-off-by: Chen Gang <[email protected]>
>> --
>> kernel/sys.c | 15 +++++----------
>> 1 files changed, 5 insertions(+), 10 deletions(-)
>
> Making a change like this might have dramatic effects. So, a few
> questions, to help better understand:
>
> How long as the behavior been this way on Linux?
> What is the origin of the documentation that states it differently?
> Do existing userspace tools already depend on the current behavior?
> What specific problem will be solved by changing this?
>
Firstly, your questions are necessary and valuable to get reply.
But sorry, I only find it by reading code, and not quite familiar with
the details (e.g. how to use it, and its history ...).
I only know the original implementation has a kernel API related issue
(at least now, it is), and try to send a possible fixing patch to
mailing list for getting more discussion and better fixing.
So, welcome any members to help reply your valuable questions (and I
should also try to answer them, but at least, I really needs time to
familiar about it).
> Thanks,
>
> -Kees
>
Thanks.
--
Chen Gang
On 08/07/2013 04:21 AM, Andy Lutomirski wrote:
> On 08/06/2013 01:01 AM, Chen Gang wrote:
>> According to the API definition, when error occurs, need return current
>> fsgid instead of the previous one.
>>
>> The related informations ("man setfsgid"):
>>
>> RETURN VALUE
>> On success, the previous value of fsgid is returned. On error, the current value of fsgid is returned.
>>
>> Signed-off-by: Chen Gang <[email protected]>
>> ---
>> kernel/sys.c | 6 +++---
>> 1 files changed, 3 insertions(+), 3 deletions(-)
>>
>> diff --git a/kernel/sys.c b/kernel/sys.c
>> index 771129b..9356dc8 100644
>> --- a/kernel/sys.c
>> +++ b/kernel/sys.c
>> @@ -775,11 +775,11 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
>>
>> kgid = make_kgid(old->user_ns, gid);
>> if (!gid_valid(kgid))
>> - return old_fsgid;
>> + return gid;
>
> Huh? So if 1234567 is not a valid gid, then setfsgid(1234567) is
> supposed to return 1234567? This makes no sense.
>
Hmm... one explanation is "current value" means the value which will be
set, it may be incorrect, if so, need return with failure (at least, it
is not conflict with the meaning of "current value").
For using:
Assume the caller's new fsgid is different with the previous one (or the caller need/should not call it).
If the return value is not equal to the new fsgid, that means the system call succeeds (it is the previous one which may be useful for caller, too).
If the return value is equal to the new fsgid (the new fsgid may '1234567'), that means the system call fails.
So at least, "man page" description is correct.
> I assume that what the man page means is that the return value is
> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
> the return value is still "current".
>
Hmm... which 'variable' can be qualified as "current value" ?
I think, at least it should match 3 requirements.
1. it must be different with "the previous value" (or "current value" is meaningless).
2. it must be known about by the caller (or caller can not check whether fails or not).
3. its meaning should not be conflict with "current value" (if "current value" is incorrect, it should be fail).
It seems only the parameter gid which caller inputs is qualified with
the 3 requirements.
> (FWIW, this behavior is awful and is probably the cause of a security
> bug or three, since success and failure are indistinguishable. But I
> think your patch is wrong.)
>
Hmm... at least, the original implementation is incorrect, and we need
discussing for this issues.
> --Andy
>
>
Thanks.
--
Chen Gang
According to the related discussion in another thread, this patch can be
obsoleted now.
Thanks.
On 08/06/2013 04:43 PM, Chen Gang wrote:
> Improve the usage of return value, so not only can make code clearer
> to readers, but also can improve the performance.
>
> Assign the return error code only when error occurs, so can save some
> instructions.
>
> For getcpu(), uname(), newuname(), and override_release(), can remove
> the return variable to make code clearer and save some instructions.
>
> Also modify the related code to pass "./scripts/checkpatch.pl" checking.
>
>
> Signed-off-by: Chen Gang <[email protected]>
> ---
> kernel/sys.c | 147 +++++++++++++++++++++++++++++++---------------------------
> 1 files changed, 79 insertions(+), 68 deletions(-)
>
> diff --git a/kernel/sys.c b/kernel/sys.c
> index 771129b..a63a350 100644
> --- a/kernel/sys.c
> +++ b/kernel/sys.c
> @@ -386,13 +386,14 @@ SYSCALL_DEFINE1(setgid, gid_t, gid)
> return -ENOMEM;
> old = current_cred();
>
> - retval = -EPERM;
> if (nsown_capable(CAP_SETGID))
> new->gid = new->egid = new->sgid = new->fsgid = kgid;
> else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid))
> new->egid = new->fsgid = kgid;
> - else
> + else {
> + retval = -EPERM;
> goto error;
> + }
>
> return commit_creds(new);
>
> @@ -533,7 +534,6 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)
> return -ENOMEM;
> old = current_cred();
>
> - retval = -EPERM;
> if (nsown_capable(CAP_SETUID)) {
> new->suid = new->uid = kuid;
> if (!uid_eq(kuid, old->uid)) {
> @@ -542,6 +542,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)
> goto error;
> }
> } else if (!uid_eq(kuid, old->uid) && !uid_eq(kuid, new->suid)) {
> + retval = -EPERM;
> goto error;
> }
>
> @@ -919,31 +920,35 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
> */
> write_lock_irq(&tasklist_lock);
>
> - err = -ESRCH;
> p = find_task_by_vpid(pid);
> - if (!p)
> + if (!p) {
> + err = -ESRCH;
> goto out;
> + }
>
> - err = -EINVAL;
> - if (!thread_group_leader(p))
> + if (!thread_group_leader(p)) {
> + err = -EINVAL;
> goto out;
> + }
>
> if (same_thread_group(p->real_parent, group_leader)) {
> - err = -EPERM;
> - if (task_session(p) != task_session(group_leader))
> + if (task_session(p) != task_session(group_leader)) {
> + err = -EPERM;
> goto out;
> - err = -EACCES;
> - if (p->did_exec)
> + }
> + if (p->did_exec) {
> + err = -EACCES;
> goto out;
> - } else {
> + }
> + } else if (p != group_leader) {
> err = -ESRCH;
> - if (p != group_leader)
> - goto out;
> + goto out;
> }
>
> - err = -EPERM;
> - if (p->signal->leader)
> + if (p->signal->leader) {
> + err = -EPERM;
> goto out;
> + }
>
> pgrp = task_pid(p);
> if (pgid != pid) {
> @@ -951,8 +956,10 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
>
> pgrp = find_vpid(pgid);
> g = pid_task(pgrp, PIDTYPE_PGID);
> - if (!g || task_session(g) != task_session(group_leader))
> + if (!g || task_session(g) != task_session(group_leader)) {
> + err = -EPERM;
> goto out;
> + }
> }
>
> err = security_task_setpgid(p, pgid);
> @@ -962,7 +969,6 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
> if (task_pgrp(p) != pgrp)
> change_pid(p, PIDTYPE_PGID, pgrp);
>
> - err = 0;
> out:
> /* All paths lead to here, thus we are safe. -DaveM */
> write_unlock_irq(&tasklist_lock);
> @@ -980,14 +986,16 @@ SYSCALL_DEFINE1(getpgid, pid_t, pid)
> if (!pid)
> grp = task_pgrp(current);
> else {
> - retval = -ESRCH;
> p = find_task_by_vpid(pid);
> - if (!p)
> + if (!p) {
> + retval = -ESRCH;
> goto out;
> + }
> grp = task_pgrp(p);
> - if (!grp)
> + if (!grp) {
> + retval = -ESRCH;
> goto out;
> -
> + }
> retval = security_task_getpgid(p);
> if (retval)
> goto out;
> @@ -1017,14 +1025,16 @@ SYSCALL_DEFINE1(getsid, pid_t, pid)
> if (!pid)
> sid = task_session(current);
> else {
> - retval = -ESRCH;
> p = find_task_by_vpid(pid);
> - if (!p)
> + if (!p) {
> + retval = -ESRCH;
> goto out;
> + }
> sid = task_session(p);
> - if (!sid)
> + if (!sid) {
> + retval = -ESRCH;
> goto out;
> -
> + }
> retval = security_task_getsid(p);
> if (retval)
> goto out;
> @@ -1096,8 +1106,6 @@ DECLARE_RWSEM(uts_sem);
> */
> static int override_release(char __user *release, size_t len)
> {
> - int ret = 0;
> -
> if (current->personality & UNAME26) {
> const char *rest = UTS_RELEASE;
> char buf[65] = { 0 };
> @@ -1115,25 +1123,25 @@ static int override_release(char __user *release, size_t len)
> v = ((LINUX_VERSION_CODE >> 8) & 0xff) + 40;
> copy = clamp_t(size_t, len, 1, sizeof(buf));
> copy = scnprintf(buf, copy, "2.6.%u%s", v, rest);
> - ret = copy_to_user(release, buf, copy + 1);
> + return copy_to_user(release, buf, copy + 1);
> }
> - return ret;
> + return 0;
> }
>
> SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
> {
> - int errno = 0;
> -
> down_read(&uts_sem);
> - if (copy_to_user(name, utsname(), sizeof *name))
> - errno = -EFAULT;
> + if (copy_to_user(name, utsname(), sizeof(*name))) {
> + up_read(&uts_sem);
> + return -EFAULT;
> + }
> up_read(&uts_sem);
>
> - if (!errno && override_release(name->release, sizeof(name->release)))
> - errno = -EFAULT;
> - if (!errno && override_architecture(name))
> - errno = -EFAULT;
> - return errno;
> + if (override_release(name->release, sizeof(name->release)))
> + return -EFAULT;
> + if (override_architecture(name))
> + return -EFAULT;
> + return 0;
> }
>
> #ifdef __ARCH_WANT_SYS_OLD_UNAME
> @@ -1142,21 +1150,21 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
> */
> SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
> {
> - int error = 0;
> -
> if (!name)
> return -EFAULT;
>
> down_read(&uts_sem);
> - if (copy_to_user(name, utsname(), sizeof(*name)))
> - error = -EFAULT;
> + if (copy_to_user(name, utsname(), sizeof(*name))) {
> + up_read(&uts_sem);
> + return -EFAULT;
> + }
> up_read(&uts_sem);
>
> - if (!error && override_release(name->release, sizeof(name->release)))
> - error = -EFAULT;
> - if (!error && override_architecture(name))
> - error = -EFAULT;
> - return error;
> + if (override_release(name->release, sizeof(name->release)))
> + return -EFAULT;
> + if (override_architecture(name))
> + return -EFAULT;
> + return 0;
> }
>
> SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
> @@ -1185,12 +1193,14 @@ SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
> __OLD_UTS_LEN);
> error |= __put_user(0, name->machine + __OLD_UTS_LEN);
> up_read(&uts_sem);
> + if (error)
> + return -EFAULT;
>
> - if (!error && override_architecture(name))
> - error = -EFAULT;
> - if (!error && override_release(name->release, sizeof(name->release)))
> - error = -EFAULT;
> - return error ? -EFAULT : 0;
> + if (override_architecture(name))
> + return -EFAULT;
> + if (override_release(name->release, sizeof(name->release)))
> + return -EFAULT;
> + return 0;
> }
> #endif
>
> @@ -1648,10 +1658,11 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
> * sure that this one is executable as well, to avoid breaking an
> * overall picture.
> */
> - err = -EACCES;
> - if (!S_ISREG(inode->i_mode) ||
> - exe.file->f_path.mnt->mnt_flags & MNT_NOEXEC)
> + if (!S_ISREG(inode->i_mode) ||
> + exe.file->f_path.mnt->mnt_flags & MNT_NOEXEC) {
> + err = -EACCES;
> goto exit;
> + }
>
> err = inode_permission(inode, MAY_EXEC);
> if (err)
> @@ -1662,15 +1673,16 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
> /*
> * Forbid mm->exe_file change if old file still mapped.
> */
> - err = -EBUSY;
> if (mm->exe_file) {
> struct vm_area_struct *vma;
>
> for (vma = mm->mmap; vma; vma = vma->vm_next)
> if (vma->vm_file &&
> path_equal(&vma->vm_file->f_path,
> - &mm->exe_file->f_path))
> + &mm->exe_file->f_path)) {
> + err = -EBUSY;
> goto exit_unlock;
> + }
> }
>
> /*
> @@ -1679,11 +1691,10 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
> * could make a snapshot over all processes running and monitor
> * /proc/pid/exe changes to notice unusual activity if needed.
> */
> - err = -EPERM;
> - if (test_and_set_bit(MMF_EXE_FILE_CHANGED, &mm->flags))
> + if (test_and_set_bit(MMF_EXE_FILE_CHANGED, &mm->flags)) {
> + err = -EPERM;
> goto exit_unlock;
> -
> - err = 0;
> + }
> set_mm_exe_file(mm, exe.file); /* this grabs a reference to exe.file */
> exit_unlock:
> up_write(&mm->mmap_sem);
> @@ -2009,13 +2020,13 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
> SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
> struct getcpu_cache __user *, unused)
> {
> - int err = 0;
> int cpu = raw_smp_processor_id();
> - if (cpup)
> - err |= put_user(cpu, cpup);
> - if (nodep)
> - err |= put_user(cpu_to_node(cpu), nodep);
> - return err ? -EFAULT : 0;
> +
> + if (cpup && put_user(cpu, cpup))
> + return -EFAULT;
> + if (nodep && put_user(cpu_to_node(cpu), nodep))
> + return -EFAULT;
> + return 0;
> }
>
> /**
>
--
Chen Gang
On 08/06, Andy Lutomirski wrote:
>
> I assume that what the man page means is that the return value is
> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
> the return value is still "current".
Probably... Still
On success, the previous value of fsuid is returned.
On error, the current value of fsuid is returned.
looks confusing. sys_setfsuid() always returns the old value.
> (FWIW, this behavior is awful and is probably the cause of a security
> bug or three, since success and failure are indistinguishable.
At least this all looks strange.
I dunno if we can change this old behaviour. I won't be surprized
if someone already uses setfsuid(-1) as getfsuid().
And perhaps the man page should be changed. Add Michael.
Oleg.
On Wed, Aug 7, 2013 at 9:21 AM, Oleg Nesterov <[email protected]> wrote:
> On 08/06, Andy Lutomirski wrote:
>>
>> I assume that what the man page means is that the return value is
>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>> the return value is still "current".
>
> Probably... Still
>
> On success, the previous value of fsuid is returned.
> On error, the current value of fsuid is returned.
>
> looks confusing. sys_setfsuid() always returns the old value.
>
>> (FWIW, this behavior is awful and is probably the cause of a security
>> bug or three, since success and failure are indistinguishable.
>
> At least this all looks strange.
>
> I dunno if we can change this old behaviour. I won't be surprized
> if someone already uses setfsuid(-1) as getfsuid().
I suspect that changing this without introducing security or other
bugs is impossible. If someone wanted to add new_setfsuid that
returned an error when it failed, that would be a different story.
(I'm not going to do that myself.)
>
> And perhaps the man page should be changed. Add Michael.
Agreed. The text is a bit odd.
>
> Oleg.
>
--
Andy Lutomirski
AMA Capital Management, LLC
On 08/08/2013 12:58 AM, Andy Lutomirski wrote:
> On Wed, Aug 7, 2013 at 9:21 AM, Oleg Nesterov <[email protected]> wrote:
>> On 08/06, Andy Lutomirski wrote:
>>>
>>> I assume that what the man page means is that the return value is
>>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>>> the return value is still "current".
>>
>> Probably... Still
>>
>> On success, the previous value of fsuid is returned.
>> On error, the current value of fsuid is returned.
>>
>> looks confusing. sys_setfsuid() always returns the old value.
>>
>>> (FWIW, this behavior is awful and is probably the cause of a security
>>> bug or three, since success and failure are indistinguishable.
>>
>> At least this all looks strange.
>>
>> I dunno if we can change this old behaviour. I won't be surprized
>> if someone already uses setfsuid(-1) as getfsuid().
>
Oh, really it is.
Hmm... as a pair function, we need add getfsuid() too, if we do not add
it, it will make negative effect with setfsuid().
Since it is a system call, we have to keep compitable.
So in my opinion, better add getfsuid2()/setfsuid2() instead of current
setfsuid()
for getfsuid2() can just reference to getuid() definition.
for setfsuid2() can just reference to setuid() definition.
(which can return error code when failure occurs).
If possible, I will/should sent the related patches for it.
> I suspect that changing this without introducing security or other
> bugs is impossible. If someone wanted to add new_setfsuid that
> returned an error when it failed, that would be a different story.
> (I'm not going to do that myself.)
>
>>
>> And perhaps the man page should be changed. Add Michael.
>
> Agreed. The text is a bit odd.
>
I agree that the text is odd, since it is an system call API, we have
to change it to match our current kernel implementation which is:
"for system call, whether succeed or not, it always return the previous value, the caller can not know whether succeed or not directly"
"if the caller need know about success or not, the demo like below"
" setfsuid(new);"
" if (setfsuid(-1) != new)"
" /* report failure */"
And better to give an additional comment:
"currently, new system call getfsuid2()/setfsuid2() are supplied, setfsuid() is obseleted"
>>
>> Oleg.
>>
>
>
>
Thanks.
--
Chen Gang
On Wed, Aug 7, 2013 at 6:30 PM, Chen Gang <[email protected]> wrote:
> On 08/08/2013 12:58 AM, Andy Lutomirski wrote:
>> On Wed, Aug 7, 2013 at 9:21 AM, Oleg Nesterov <[email protected]> wrote:
>>> On 08/06, Andy Lutomirski wrote:
>>>>
>>>> I assume that what the man page means is that the return value is
>>>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>>>> the return value is still "current".
>>>
>>> Probably... Still
>>>
>>> On success, the previous value of fsuid is returned.
>>> On error, the current value of fsuid is returned.
>>>
>>> looks confusing. sys_setfsuid() always returns the old value.
>>>
>>>> (FWIW, this behavior is awful and is probably the cause of a security
>>>> bug or three, since success and failure are indistinguishable.
>>>
>>> At least this all looks strange.
>>>
>>> I dunno if we can change this old behaviour. I won't be surprized
>>> if someone already uses setfsuid(-1) as getfsuid().
>>
>
> Oh, really it is.
>
> Hmm... as a pair function, we need add getfsuid() too, if we do not add
> it, it will make negative effect with setfsuid().
>
> Since it is a system call, we have to keep compitable.
>
> So in my opinion, better add getfsuid2()/setfsuid2() instead of current
> setfsuid()
How about getfsuid() and setfsuid2()?
--Andy
On 08/08/2013 09:35 AM, Andy Lutomirski wrote:
> On Wed, Aug 7, 2013 at 6:30 PM, Chen Gang <[email protected]> wrote:
>> On 08/08/2013 12:58 AM, Andy Lutomirski wrote:
>>> On Wed, Aug 7, 2013 at 9:21 AM, Oleg Nesterov <[email protected]> wrote:
>>>> On 08/06, Andy Lutomirski wrote:
>>>>>
>>>>> I assume that what the man page means is that the return value is
>>>>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>>>>> the return value is still "current".
>>>>
>>>> Probably... Still
>>>>
>>>> On success, the previous value of fsuid is returned.
>>>> On error, the current value of fsuid is returned.
>>>>
>>>> looks confusing. sys_setfsuid() always returns the old value.
>>>>
>>>>> (FWIW, this behavior is awful and is probably the cause of a security
>>>>> bug or three, since success and failure are indistinguishable.
>>>>
>>>> At least this all looks strange.
>>>>
>>>> I dunno if we can change this old behaviour. I won't be surprized
>>>> if someone already uses setfsuid(-1) as getfsuid().
>>>
>>
>> Oh, really it is.
>>
>> Hmm... as a pair function, we need add getfsuid() too, if we do not add
>> it, it will make negative effect with setfsuid().
>>
>> Since it is a system call, we have to keep compitable.
>>
>> So in my opinion, better add getfsuid2()/setfsuid2() instead of current
>> setfsuid()
>
> How about getfsuid() and setfsuid2()?
>
Hmm... I have 2 reasons, please check.
1st reason: I checked history (just like Kees Cook suggested),
getfsuid() is mentioned before (you can google to find it), so need use
getfsuid2() to bypass the history complex.
And 2nd reason: getfsuid() seems more like the pair of setfsuid(), not
for setfsuid2().
> --Andy
>
>
Thanks.
--
Chen Gang
On 08/07/13 18:21, Oleg Nesterov wrote:
> On 08/06, Andy Lutomirski wrote:
>>
>> I assume that what the man page means is that the return value is
>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>> the return value is still "current".
>
> Probably... Still
>
> On success, the previous value of fsuid is returned.
> On error, the current value of fsuid is returned.
>
> looks confusing. sys_setfsuid() always returns the old value.
>
>> (FWIW, this behavior is awful and is probably the cause of a security
>> bug or three, since success and failure are indistinguishable.
>
> At least this all looks strange.
>
> I dunno if we can change this old behaviour. I won't be surprized
> if someone already uses setfsuid(-1) as getfsuid().
>
> And perhaps the man page should be changed. Add Michael.
Thanks, Oleg. I've applied the following patch to setfsuid.2
(and a similar patch to setfsgid.2).
Cheers,
Michael
--- a/man2/setfsuid.2
+++ b/man2/setfsuid.2
@@ -67,12 +67,8 @@ matches either the real user ID, effective user ID, saved set-user-ID, or
the current value of
.IR fsuid .
.SH RETURN VALUE
-On success, the previous value of
-.I fsuid
-is returned.
-On error, the current value of
-.I fsuid
-is returned.
+On both success and failure,
+this call returns the previous filesystem user ID of the caller.
.SH VERSIONS
This system call is present in Linux since version 1.2.
.\" This system call is present since Linux 1.1.44
@@ -102,7 +98,16 @@ The glibc
.BR setfsuid ()
wrapper function transparently deals with the variation across kernel versions.
.SH BUGS
-No error messages of any kind are returned to the caller.
+No error indications of any kind are returned to the caller,
+and the fact that both successful and unsuccessful calls return
+the same value makes it impossible to directly determine
+whether the call succeeded or failed.
+Instead, the caller must resort to looking at the return value
+from a further call such as
+.IR setfsuid(\-1)
+(which will always fail), in order to determine if a preceding call to
+.BR setfsuid ()
+changed the filesystem user ID.
At the very
least,
.B EPERM
On 08/08/13 03:48, Chen Gang wrote:
> On 08/08/2013 09:35 AM, Andy Lutomirski wrote:
>> On Wed, Aug 7, 2013 at 6:30 PM, Chen Gang <[email protected]> wrote:
>>> On 08/08/2013 12:58 AM, Andy Lutomirski wrote:
>>>> On Wed, Aug 7, 2013 at 9:21 AM, Oleg Nesterov <[email protected]> wrote:
>>>>> On 08/06, Andy Lutomirski wrote:
>>>>>>
>>>>>> I assume that what the man page means is that the return value is
>>>>>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>>>>>> the return value is still "current".
>>>>>
>>>>> Probably... Still
>>>>>
>>>>> On success, the previous value of fsuid is returned.
>>>>> On error, the current value of fsuid is returned.
>>>>>
>>>>> looks confusing. sys_setfsuid() always returns the old value.
>>>>>
>>>>>> (FWIW, this behavior is awful and is probably the cause of a security
>>>>>> bug or three, since success and failure are indistinguishable.
>>>>>
>>>>> At least this all looks strange.
>>>>>
>>>>> I dunno if we can change this old behaviour. I won't be surprized
>>>>> if someone already uses setfsuid(-1) as getfsuid().
>>>>
>>>
>>> Oh, really it is.
>>>
>>> Hmm... as a pair function, we need add getfsuid() too, if we do not add
>>> it, it will make negative effect with setfsuid().
>>>
>>> Since it is a system call, we have to keep compitable.
>>>
>>> So in my opinion, better add getfsuid2()/setfsuid2() instead of current
>>> setfsuid()
>>
>> How about getfsuid() and setfsuid2()?
>>
>
> Hmm... I have 2 reasons, please check.
>
> 1st reason: I checked history (just like Kees Cook suggested),
> getfsuid() is mentioned before (you can google to find it), so need use
> getfsuid2() to bypass the history complex.
>
> And 2nd reason: getfsuid() seems more like the pair of setfsuid(), not
> for setfsuid2().
Time to apply the brakes... *Why* add new system calls here? I don't
think there is any good reason. Yes, the existing APIs are rubbish,
but, as far as I can tell, they are also obsolete and unneeded.
The fsuid/fsgid mechanism was a bizarre Linuxism whose only purpose
was (as far as I know), to allow for the fact that Linux long ago
applied nonstandard rules when determining when one process could
send signals to another. Quoting some book on the subject:
Why does Linux provide the file-system IDs and in what
circumstances would we want the effective and file-system
IDs to differ? The reasons are primarily historical.
The file-system IDs first appeared in Linux 1.2. In
that kernel version, one process could send a signal to
another if the effective user ID of the sender matched
the real or effective user ID of the target process.
This affected certain programs such as the Linux NFS
(Network File System) server program, which needed to be
able to access files as though it had the effective IDs
of the corresponding client process. However, if the NFS
server changed its effective user ID, it would be
vulnerable to signals from unprivileged user processes.
To prevent this possibility, the separate file-system user
and group IDs were devised. By leaving its effective IDs
unchanged, but changing its file-system IDs, the NFS
server could masquerade as another user for the purpose of
accessing files without being vulnerable to signals from
user processes.
From kernel 2.0 onward, Linux adopted the SUSv3-mandated
rules regarding permission for sending signals, and these
rules don't involve the effective user ID of the target
process. Thus, the file-system ID feature is no longer
strictly necessary (a process can nowadays achieve the
desired results by making judicious use of the system
calls described later in this chapter to change
the value of the effective user ID to and from an
unprivileged value, as required), but it remains for
compatibility with existing software.
So, I don't think anything needs fixing: there should be no
new users of these system calls anyway.
Cheers,
Michael
--
Michael Kerrisk Linux man-pages maintainer;
http://www.kernel.org/doc/man-pages/
Author of "The Linux Programming Interface", http://blog.man7.org/
On 08/08/2013 09:52 PM, Michael Kerrisk (man-pages) wrote:
> On 08/08/13 03:48, Chen Gang wrote:
>> On 08/08/2013 09:35 AM, Andy Lutomirski wrote:
>>> On Wed, Aug 7, 2013 at 6:30 PM, Chen Gang <[email protected]> wrote:
>>>> On 08/08/2013 12:58 AM, Andy Lutomirski wrote:
>>>>> On Wed, Aug 7, 2013 at 9:21 AM, Oleg Nesterov <[email protected]> wrote:
>>>>>> On 08/06, Andy Lutomirski wrote:
>>>>>>>
>>>>>>> I assume that what the man page means is that the return value is
>>>>>>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>>>>>>> the return value is still "current".
>>>>>>
>>>>>> Probably... Still
>>>>>>
>>>>>> On success, the previous value of fsuid is returned.
>>>>>> On error, the current value of fsuid is returned.
>>>>>>
>>>>>> looks confusing. sys_setfsuid() always returns the old value.
>>>>>>
>>>>>>> (FWIW, this behavior is awful and is probably the cause of a security
>>>>>>> bug or three, since success and failure are indistinguishable.
>>>>>>
>>>>>> At least this all looks strange.
>>>>>>
>>>>>> I dunno if we can change this old behaviour. I won't be surprized
>>>>>> if someone already uses setfsuid(-1) as getfsuid().
>>>>>
>>>>
>>>> Oh, really it is.
>>>>
>>>> Hmm... as a pair function, we need add getfsuid() too, if we do not add
>>>> it, it will make negative effect with setfsuid().
>>>>
>>>> Since it is a system call, we have to keep compitable.
>>>>
>>>> So in my opinion, better add getfsuid2()/setfsuid2() instead of current
>>>> setfsuid()
>>>
>>> How about getfsuid() and setfsuid2()?
>>>
>>
>> Hmm... I have 2 reasons, please check.
>>
>> 1st reason: I checked history (just like Kees Cook suggested),
>> getfsuid() is mentioned before (you can google to find it), so need use
>> getfsuid2() to bypass the history complex.
>>
>> And 2nd reason: getfsuid() seems more like the pair of setfsuid(), not
>> for setfsuid2().
>
> Time to apply the brakes... *Why* add new system calls here? I don't
> think there is any good reason. Yes, the existing APIs are rubbish,
> but, as far as I can tell, they are also obsolete and unneeded.
> The fsuid/fsgid mechanism was a bizarre Linuxism whose only purpose
> was (as far as I know), to allow for the fact that Linux long ago
> applied nonstandard rules when determining when one process could
> send signals to another. Quoting some book on the subject:
>
> Why does Linux provide the file-system IDs and in what
> circumstances would we want the effective and file-system
> IDs to differ? The reasons are primarily historical.
> The file-system IDs first appeared in Linux 1.2. In
> that kernel version, one process could send a signal to
> another if the effective user ID of the sender matched
> the real or effective user ID of the target process.
> This affected certain programs such as the Linux NFS
> (Network File System) server program, which needed to be
> able to access files as though it had the effective IDs
> of the corresponding client process. However, if the NFS
> server changed its effective user ID, it would be
> vulnerable to signals from unprivileged user processes.
> To prevent this possibility, the separate file-system user
> and group IDs were devised. By leaving its effective IDs
> unchanged, but changing its file-system IDs, the NFS
> server could masquerade as another user for the purpose of
> accessing files without being vulnerable to signals from
> user processes.
>
> From kernel 2.0 onward, Linux adopted the SUSv3-mandated
> rules regarding permission for sending signals, and these
> rules don't involve the effective user ID of the target
> process. Thus, the file-system ID feature is no longer
> strictly necessary (a process can nowadays achieve the
> desired results by making judicious use of the system
> calls described later in this chapter to change
> the value of the effective user ID to and from an
> unprivileged value, as required), but it remains for
> compatibility with existing software.
>
> So, I don't think anything needs fixing: there should be no
> new users of these system calls anyway.
>
OK, thank you for your details, at least for me, what you says above
sounds reasonable.
> Cheers,
>
> Michael
>
>
Thanks.
--
Chen Gang
On 08/08/2013 09:37 PM, Michael Kerrisk (man-pages) wrote:
> On 08/07/13 18:21, Oleg Nesterov wrote:
>> On 08/06, Andy Lutomirski wrote:
>>>
>>> I assume that what the man page means is that the return value is
>>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>>> the return value is still "current".
>>
>> Probably... Still
>>
>> On success, the previous value of fsuid is returned.
>> On error, the current value of fsuid is returned.
>>
>> looks confusing. sys_setfsuid() always returns the old value.
>>
>>> (FWIW, this behavior is awful and is probably the cause of a security
>>> bug or three, since success and failure are indistinguishable.
>>
>> At least this all looks strange.
>>
>> I dunno if we can change this old behaviour. I won't be surprized
>> if someone already uses setfsuid(-1) as getfsuid().
>>
>> And perhaps the man page should be changed. Add Michael.
>
> Thanks, Oleg. I've applied the following patch to setfsuid.2
> (and a similar patch to setfsgid.2).
>
> Cheers,
>
> Michael
>
> --- a/man2/setfsuid.2
> +++ b/man2/setfsuid.2
> @@ -67,12 +67,8 @@ matches either the real user ID, effective user ID, saved set-user-ID, or
> the current value of
> .IR fsuid .
> .SH RETURN VALUE
> -On success, the previous value of
> -.I fsuid
> -is returned.
> -On error, the current value of
> -.I fsuid
> -is returned.
> +On both success and failure,
> +this call returns the previous filesystem user ID of the caller.
> .SH VERSIONS
> This system call is present in Linux since version 1.2.
> .\" This system call is present since Linux 1.1.44
> @@ -102,7 +98,16 @@ The glibc
> .BR setfsuid ()
> wrapper function transparently deals with the variation across kernel versions.
> .SH BUGS
> -No error messages of any kind are returned to the caller.
> +No error indications of any kind are returned to the caller,
> +and the fact that both successful and unsuccessful calls return
> +the same value makes it impossible to directly determine
> +whether the call succeeded or failed.
> +Instead, the caller must resort to looking at the return value
> +from a further call such as
> +.IR setfsuid(\-1)
> +(which will always fail), in order to determine if a preceding call to
> +.BR setfsuid ()
> +changed the filesystem user ID.
> At the very
> least,
> .B EPERM
>
Is it suitable to mention this API is obsoleted and unneeded in man page
? ;-)
Thanks.
--
Chen Gang
On 08/09/13 02:59, Chen Gang wrote:
> On 08/08/2013 09:37 PM, Michael Kerrisk (man-pages) wrote:
>> On 08/07/13 18:21, Oleg Nesterov wrote:
>>> On 08/06, Andy Lutomirski wrote:
>>>>
>>>> I assume that what the man page means is that the return value is
>>>> whatever fsgid was prior to the call. On error, fsgid isn't changed, so
>>>> the return value is still "current".
>>>
>>> Probably... Still
>>>
>>> On success, the previous value of fsuid is returned.
>>> On error, the current value of fsuid is returned.
>>>
>>> looks confusing. sys_setfsuid() always returns the old value.
>>>
>>>> (FWIW, this behavior is awful and is probably the cause of a security
>>>> bug or three, since success and failure are indistinguishable.
>>>
>>> At least this all looks strange.
>>>
>>> I dunno if we can change this old behaviour. I won't be surprized
>>> if someone already uses setfsuid(-1) as getfsuid().
>>>
>>> And perhaps the man page should be changed. Add Michael.
>>
>> Thanks, Oleg. I've applied the following patch to setfsuid.2
>> (and a similar patch to setfsgid.2).
>>
>> Cheers,
>>
>> Michael
>>
>> --- a/man2/setfsuid.2
>> +++ b/man2/setfsuid.2
>> @@ -67,12 +67,8 @@ matches either the real user ID, effective user ID, saved set-user-ID, or
>> the current value of
>> .IR fsuid .
>> .SH RETURN VALUE
>> -On success, the previous value of
>> -.I fsuid
>> -is returned.
>> -On error, the current value of
>> -.I fsuid
>> -is returned.
>> +On both success and failure,
>> +this call returns the previous filesystem user ID of the caller.
>> .SH VERSIONS
>> This system call is present in Linux since version 1.2.
>> .\" This system call is present since Linux 1.1.44
>> @@ -102,7 +98,16 @@ The glibc
>> .BR setfsuid ()
>> wrapper function transparently deals with the variation across kernel versions.
>> .SH BUGS
>> -No error messages of any kind are returned to the caller.
>> +No error indications of any kind are returned to the caller,
>> +and the fact that both successful and unsuccessful calls return
>> +the same value makes it impossible to directly determine
>> +whether the call succeeded or failed.
>> +Instead, the caller must resort to looking at the return value
>> +from a further call such as
>> +.IR setfsuid(\-1)
>> +(which will always fail), in order to determine if a preceding call to
>> +.BR setfsuid ()
>> +changed the filesystem user ID.
>> At the very
>> least,
>> .B EPERM
>>
>
> Is it suitable to mention this API is obsoleted and unneeded in man page
> ? ;-)
Yes, that seems reasonable. Done.
Cheers,
Michael