The userland_sysctl() function retries sysctl_root() until returned

error is not EAGAIN. Several sysctls that inspect another process use
p_candebug() for checking access right for the curproc. p_candebug()
returns EAGAIN for some reasons, in particular, for the process doing
exec() now. If execing process tries to lock Giant, we get a livelock,
because sysctl handlers are covered by Giant, and often do not sleep.

Break the livelock by dropping Giant and allowing other threads to
execute in the EAGAIN loop.

Also, do not return EAGAIN from p_candebug() when process is executing,
use more appropriate EBUSY error [1].

Reported and tested by:	pho
Suggested by:	rwatson [1]
Reviewed by:	rwatson, des
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2008-12-12 12:06:28 +00:00
parent 5367b241cd
commit af80b2c901
2 changed files with 9 additions and 3 deletions

View file

@ -1679,7 +1679,7 @@ p_candebug(struct thread *td, struct proc *p)
* should be moved to the caller's of p_candebug().
*/
if ((p->p_flag & P_INEXEC) != 0)
return (EAGAIN);
return (EBUSY);
return (0);
}

View file

@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/sx.h>
#include <sys/sysproto.h>
#include <sys/uio.h>
#include <sys/vimage.h>
#include <security/mac/mac_framework.h>
@ -1416,11 +1417,16 @@ userland_sysctl(struct thread *td, int *name, u_int namelen, void *old,
SYSCTL_LOCK();
CURVNET_SET(TD_TO_VNET(curthread));
do {
for (;;) {
req.oldidx = 0;
req.newidx = 0;
error = sysctl_root(0, name, namelen, &req);
} while (error == EAGAIN);
if (error != EAGAIN)
break;
DROP_GIANT();
uio_yield();
PICKUP_GIANT();
}
if (req.lock == REQ_WIRED && req.validlen > 0)
vsunlock(req.oldptr, req.validlen);