Neohapsis is currently accepting applications for employment. For more information, please visit our website www.neohapsis.com or email email@example.com
From: Glynn Clements (glynn.clements_at_virgin.net)
Date: Fri Jan 10 2003 - 14:32:52 CST
Timo Sirainen wrote:
> > > Looks like once a process has called setuid(), no-one except root can
> > > ptrace() it. I don't see this mentioned very clearly in any man page
> > > though (*BSD, Linux).
> > my ptrace(2) page on debian woody says this:
> > ERRORS
> > EPERM The specified process cannot be traced. This could be because
> > the parent has insufficient privileges; non-root processes cannot
> > trace processes that they cannot send signals to or those
> > running setuid/setgid programs, for obvious reasons.
> > Alternatively, the process may already be being traced, or be
> > init (pid 1).
> You mean the "running setuid/setgid programs"? How is setuid/setgid
> program defined? I've always thought it was just the +s bit attached to
> the file. When does the setuidness get cleared; after fork(), exec*(),
> or ..? Is that standardized somewhere?
The behaviour of programs with setuid/setgid bits is that executing
such a program with exec*() results in euid/egid being set to the
owner/group of the file. The effects persist until the program revokes
those privileges with setuid(), seteuid() etc, or it executes another
setuid/setgid program; executing a non-setgid program doesn't revoke
However, for the purpose of checking permissions for certain "unsafe"
operations (e.g. ptrace()), Linux uses a slightly broader definition.
It includes processes where uid != euid, but also those processes for
which this was previously true but isn't now. IOW, a process running a
setuid program remains untraceable even after it drops privileges, as
it might still have access to privileged resources (e.g. descriptors)
or have privileged data in memory.
IIRC, this state ends when exec*() is called (for a non setuid
program) after any excess privileges have been revoked. This should
ensure that no privileged data remains in memory (exec*() completely
replaces the process' memory space, except for the environment/argv
page), although it's up to the programmer to ensure that any
privileged resources which would survive exec*() (e.g. descriptors)
are released first.
Basically, the intention is that you can only trace "normal"
processes, i.e. those which you own completely and have always owned
completely; in case of doubt, permission is refused. However, the
"abnormal" status must get revoked eventually, otherwise (almost) all
non-root processes would be abnormal, as they ultimately inherit from
an initial process (e.g. login, telnetd, sshd) which started as root
then changed its ownership to that of the user.
In 2.4.20, the actual permission checks for ptrace() are (from
if (task->pid <= 1)
if (task == current)
if(((current->uid != task->euid) ||
(current->uid != task->suid) ||
(current->uid != task->uid) ||
(current->gid != task->egid) ||
(current->gid != task->sgid) ||
(!cap_issubset(task->cap_permitted, current->cap_permitted)) ||
(current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE))
/* the same process cannot be attached many times */
if (task->ptrace & PT_PTRACED)
However, much of the logic is hidden by the use of task->mm->dumpable;
IIRC, this determines whether a process can generate core dump, which
has many of the same security considerations as ptrace(). Examination
of kernel/sys.c indicates a number of places where this flag is
cleared (mostly where various uids/gids differ).
AFAICT, your original comment (that this isn't mentioned clearly in
any man page) is correct. The above description comes from a mixture
of comments made in various mailing lists and personal experience.
-- Glynn Clements <glynn.clementsvirgin.net>