Date: Mon, 17 Jun 2002 17:19:59 +1000 (EST) From: Bruce Evans <bde@zeta.org.au> To: Peter Jeremy <peter.jeremy@alcatel.com.au> Cc: freebsd-current@FreeBSD.ORG Subject: Re: proc-args (M_PARGS) leakage Message-ID: <20020617155522.X3493-100000@gamplex.bde.org> In-Reply-To: <20020617131502.O680@gsmx07.alcatel.com.au>
next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, 17 Jun 2002, Peter Jeremy wrote: > Having noticed that my system is paging far more than I would have > expected, I went looking and found that the 'proc-args' pool was > far larger than I expected. And is growing over time: > > gsmx07# vmstat -m|grep proc-args > proc-args701802 70634K 70634K 1589264 16,32,64,128,256 > [about 10 minutes delay] > gsmx07# vmstat -m|grep proc-args;vmstat -m|grep proc-args > proc-args702048 70652K 70652K 1589557 16,32,64,128,256 > proc-args702047 70652K 70652K 1589558 16,32,64,128,256 > gsmx07# I see a relatively slow growth on a fairly idle machine (4031K after 3 days of uptime). > Whilst I'm fairly certain it's not my problem, sysctl_kern_proc_args() > (1.136) looks dubious: > ... > PROC_LOCK(p); > pa = p->p_args; > pargs_hold(pa); > PROC_UNLOCK(p); > if (req->oldptr && pa != NULL) { > error = SYSCTL_OUT(req, pa->ar_args, pa->ar_length); > } > if (req->newptr == NULL) { > pargs_drop(pa); > return (error); > } > To this point, it all looks correct: An additional reference has been > added to p_args to allow the SYSCTL_OUT() to copy the arguments without > them being freed. The relevant pargs entry will have a ref count of at > least 2 (the original reference from 'p' and a new reference via > pargs_hold()). > > PROC_LOCK(p); > pa = p->p_args; > p->p_args = NULL; > PROC_UNLOCK(p); > pargs_drop(pa); > > (And later code shows pa dead at this point). I don't follow this. > pargs_drop(pa) deletes a single reference count - which matches the > line "p->p_args = NULL;" - but I don't see anything to match the > pargs_hold(pa) above. Yes, this seems to be missing a pargs_drop() to drop the reference that we have just gained. The corresponding code in RELENG_4 uses --p->p_args->ar_ref to drop the main reference. This was sufficient since RELENG_4 doesn't aquire another reference. I think the above should be (after fixing some other bugs (see below) and some style bugs): if (req->oldptr != NULL) { if (pa != NULL) error = SYSCTL_OUT(req, pa->ar_args, pa->ar_length); else dont_forget_to_set_output_parameters(); } pargs_drop(pa); if (error != 0 || req->newptr == NULL) return (error); #ifdef foot_shooting /* * This just loses if we can't read the new args. It doesn't even * help in the non-error case, since setting p->p_args to NULL * here doesn't keep it NULL after we release the proc lock. */ PROC_LOCK(p); pa = p->p_args; p->p_args = NULL; PROC_UNLOCK(p); pargs_drop(pa); #endif Other bugs: if (req->oldptr && pa != NULL) { error = SYSCTL_OUT(req, pa->ar_args, pa->ar_length); } This fails to set the output parameters in the req->oldptr != NULL case. It also has some style bugs (only one pointer explicitly compared with NULL, and verbose braces). if (req->newptr == NULL) { pargs_drop(pa); return (error); } This should return if error != 0. Otherwise OK until here, as above. We return if we are just reading the proc table. I would have though that this was the usual case, but on my fairly idle system it is 100% unusual: this sysctl is only used by sendmail to write sometime. PROC_LOCK(p); pa = p->p_args; p->p_args = NULL; PROC_UNLOCK(p); pargs_drop(pa); This throws away p->p_args, so we should be sure that we have a new p->p_args before doing it ... if (req->newlen + sizeof(struct pargs) > ps_arg_cache_limit) return (error); ... but here we don't even do this simple error check first. We also return a garbage error code (normally 0). pa = pargs_alloc(req->newlen); error = SYSCTL_IN(req, pa->ar_args, req->newlen); if (!error) { PROC_LOCK(p); p->p_args = pa; PROC_UNLOCK(p); This leaks p->p_args in the unlikely event that p->p_args becomes non-NULL after we have set it to NULL. The code under '#ifdef foot_shooting" should be merged here to fix this. } else pargs_free(pa); return (error); The else clause is for the error case. Again, we have thown away p->p_args too early. Bruce To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-current" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020617155522.X3493-100000>