Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 11 Jul 2001 23:51:59 -0700
From:      Dima Dorfman <dima@unixfreak.org>
To:        Matt Dillon <dillon@earth.backplane.com>
Cc:        Mark Peek <mark@whistle.com>, stable@FreeBSD.ORG
Subject:   Re: cron exited on signal 11 
Message-ID:  <20010712065159.619703E2F@bazooka.unixfreak.org>
In-Reply-To: <200107120602.f6C628T41618@earth.backplane.com>; from dillon@earth.backplane.com on "Wed, 11 Jul 2001 23:02:08 -0700 (PDT)"

next in thread | previous in thread | raw e-mail | index | archive | help
Matt Dillon <dillon@earth.backplane.com> writes:
>     Well this certainly opens up a can of worms!  Not your patch, but what 
>     it implies.  For example, env_set() assumes envp is not NULL as well.
>     In fact, most of the code assumes envp is not NULL, and 99% of the time
>     that is true due to the env_init() calls.

I think it should be 100%; why would envp be NULL?

>     I found at least one case in the code where free_entry() is called with
>     e->envp == NULL.  Perhaps a more correct fix would simply be to have
>     free_entry() check for e->envp != NULL before calling env_free() rather
>     then having env_free() check for NULL.

On the contrary, I think free_entry() should assume that all members
aren't NULL because if they're NULL, it's probably a bug (in which
case a core dump is advisable).

Below is a copy of an e-mail I sent to Mark Peek on this topic.  The
only thing I'd like to add is that the patch below addresses two
different problems.  The first is that free_entry() simply shouldn't
be called after the eof: label.  The second is that the `class' member
is unnecessarily treated inconsistently from the others.  They really
don't belong in the same patch, because the former is a bugfix while
the latter is a style/consistency fix.

I think that whether a free()-like routine should check if its
argument (or members thereof) are NULL is a religious/philosophical
thing.  We've had arguments about whether free(3) should crash if its
argument is NULL, or if it should just let it go.  Furthermore, some
programmers religiously rely on free_something() doing the right thing
with a half-filled structure, while others prefer to have the routine
filling in the structure clean up after itself.  I don't want to argue
about which approach is better, but since the original author used the
latter, I think we should stick to it unless there's a good reason
(and there isn't, really).

Right now, cron consistently uses the latter approach except for the
`class' member of `entry'; that was added by ache@, and he chose to be
inconsistent.  I think we should just fix the inconsistency.  If
free_entry() is called somewhere when `envp' is NULL, that's another
bug that should be fixed.

But again, I'd like to point out that there are two issues here: (1)
the bugfix, which is pretty straight-forward (free_entry -> free), and
(2) the inconsistency issue, which is less straight-forward.

Regards,

					Dima Dorfman
					dima@unixfreak.org


> Date:    Wed, 11 Jul 2001 21:19:07 PDT
> To:      Mark Peek <mark@whistle.com>
> From:    Dima Dorfman <dima@hornet.unixfreak.org>
> Subject: Re: cron exited on signal 11 
> 
> In-Reply-To: <p0510032db772c73d2f62@[207.76.207.129]>; from mark@whistle.com on
>      *** "Wed, 11 Jul 2001 20:27:54 -0700"
> 
> Mark Peek <mark@whistle.com> writes:
> > Index: usr.sbin/cron/lib/env.c
> > ===================================================================
> > RCS file: /cvs/freebsd/src/usr.sbin/cron/lib/env.c,v
> > retrieving revision 1.9
> > diff -u -r1.9 env.c
> > --- usr.sbin/cron/lib/env.c	2000/04/30 15:57:00	1.9
> > +++ usr.sbin/cron/lib/env.c	2001/07/12 03:12:29
> > @@ -41,6 +41,8 @@
> >   {
> >   	char	**p;
> > 
> > +	if (envp == NULL)
> > +		return;
> >   	for (p = envp;  *p;  p++)
> >   		free(*p);
> >   	free(envp);
> 
> I think this is the wrong fix; the bug is your/our original change of
> free(e) -> free_entry(e) after the eof: label.  This routine takes
> great care (but not great enough, see below) to make sure that it
> doesn't forgot to free() something in this structure before jumping to
> eof.  There's code like this (entry.c, line 397 of rev. 1.12):
> 
> :        ch = get_string(cmd, MAX_COMMAND, file, "\n");
> :
> :        /* a file without a \n before the EOF is rude, so we'll complain...
> :         */
> :        if (ch == EOF) {
> :                env_free(e->envp);
> :                ecode = e_cmd;
> :                goto eof;
> :        }
> 
> Notice how it calls env_free() before jumping to eof.  There are
> similar fragments throughout the routine.  `envp', `cmd', and `class'
> are the only members of `entry' that need to be free()'d.  The routine
> does a very good job of free()'ing `envp' and `cmd' when appropriate,
> but since `class' is an afterthough (introduced in rev. 1.8), it
> doesn't bother with that.
> 
> Thus, I think the right fix is to correct its behavior with regards to
> free()'ing `class'.  Changing free_entry() (or env_free()) not to free
> something if it isn't NULL seems wrong; it will just mask other bugs
> (e.g., if there is an `entry' floating around with one of the fields
> not filled in; it should crash, but wouldn't with your fix).
> 
> Below is a patch that demonstrates what I mean.  I haven't tested it
> beyond compiling it, but I think it should work.  It isn't very pretty
> from the point of view of the source code (those #ifdef LOGIN_CAP's
> are evil!), but I think the result is a better product.  Also note
> that not free()'ing `class' didn't result in a leak because it was
> taken care of in free_entry(); however, I think that either all or
> none members should be free()'d before jumping to eof.  The former
> (all) seems to be the better choice.
> 
> Thoughts?
> 
> Thanks,
> 
> 					Dima Dorfman
> 					dima@unixfreak.org
> 
> P.S.  I haven't carefully checked that it doesn't forget to free()
> something else.  `class' is just the obvious one.
> 
> Index: entry.c
> ===================================================================
> RCS file: /stl/src/FreeBSD/src/usr.sbin/cron/lib/entry.c,v
> retrieving revision 1.12
> diff -u -r1.12 entry.c
> --- entry.c	2001/06/13 05:49:37	1.12
> +++ entry.c	2001/07/12 04:17:29
> @@ -70,8 +70,7 @@
>  	entry	*e;
>  {
>  #ifdef LOGIN_CAP
> -	if (e->class != NULL)
> -		free(e->class);
> +	free(e->class);
>  #endif
>  	free(e->cmd);
>  	env_free(e->envp);
> @@ -291,6 +290,7 @@
>  			goto eof;
>  		}
>  		if ((lc = login_getclass(e->class)) == NULL) {
> +			free(e->class);
>  			ecode = e_class;
>  			goto eof;
>  		}
> @@ -300,6 +300,9 @@
>  		if ((s = strrchr(username, ':')) != NULL) {
>  			*s = '\0';
>  			if ((grp = getgrnam(s + 1)) == NULL) {
> +#ifdef LOGIN_CAP
> +				free(e->class);
> +#endif
>  				ecode = e_group;
>  				goto eof;
>  			}
> @@ -307,6 +310,9 @@
>  
>  		pw = getpwnam(username);
>  		if (pw == NULL) {
> +#ifdef LOGIN_CAP
> +			free(e->class);
> +#endif
>  			ecode = e_username;
>  			goto eof;
>  		}
> @@ -319,6 +325,9 @@
>  	}
>  
>  	if (pw->pw_expire && time(NULL) >= pw->pw_expire) {
> +#ifdef LOGIN_CAP
> +		free(e->class);
> +#endif
>  		ecode = e_username;
>  		goto eof;
>  	}
> @@ -332,6 +341,9 @@
>  	e->envp = env_copy(envp);
>  	if (e->envp == NULL) {
>  		warn("env_copy");
> +#ifdef LOGIN_CAP
> +		free(e->class);
> +#endif
>  		ecode = e_mem;
>  		goto eof;
>  	}
> @@ -341,6 +353,9 @@
>  		e->envp = env_set(e->envp, envstr);
>  		if (e->envp == NULL) {
>  			warn("env_set(%s)", envstr);
> +#ifdef LOGIN_CAP
> +			free(e->class);
> +#endif
>  			env_free(prev_env);
>  			ecode = e_mem;
>  			goto eof;
> @@ -351,6 +366,9 @@
>  	e->envp = env_set(e->envp, envstr);
>  	if (e->envp == NULL) {
>  		warn("env_set(%s)", envstr);
> +#ifdef LOGIN_CAP
> +		free(e->class);
> +#endif
>  		env_free(prev_env);
>  		ecode = e_mem;
>  		goto eof;
> @@ -361,6 +379,9 @@
>  		e->envp = env_set(e->envp, envstr);
>  		if (e->envp == NULL) {
>  			warn("env_set(%s)", envstr);
> +#ifdef LOGIN_CAP
> +			free(e->class);
> +#endif
>  			env_free(prev_env);
>  			ecode = e_mem;
>  			goto eof;
> @@ -371,6 +392,9 @@
>  	e->envp = env_set(e->envp, envstr);
>  	if (e->envp == NULL) {
>  		warn("env_set(%s)", envstr);
> +#ifdef LOGIN_CAP
> +		free(e->class);
> +#endif
>  		env_free(prev_env);
>  		ecode = e_mem;
>  		goto eof;
> @@ -381,6 +405,9 @@
>  	e->envp = env_set(e->envp, envstr);
>  	if (e->envp == NULL) {
>  		warn("env_set(%s)", envstr);
> +#ifdef LOGIN_CAP
> +		free(e->class);
> +#endif
>  		env_free(prev_env);
>  		ecode = e_mem;
>  		goto eof;
> @@ -399,6 +426,9 @@
>  	/* a file without a \n before the EOF is rude, so we'll complain...
>  	 */
>  	if (ch == EOF) {
> +#ifdef LOGIN_CAP
> +		free(e->class);
> +#endif
>  		env_free(e->envp);
>  		ecode = e_cmd;
>  		goto eof;
> @@ -409,6 +439,9 @@
>  	e->cmd = strdup(cmd);
>  	if (e->cmd == NULL) {
>  		warn("strdup(\"%d\")", cmd);
> +#ifdef LOGIN_CAP
> +		free(e->class);
> +#endif
>  		env_free(e->envp);
>  		ecode = e_mem;
>  		goto eof;
> @@ -420,7 +453,7 @@
>  	return e;
>  
>   eof:
> -	free_entry(e);
> +	free(e);
>  	if (ecode != e_none && error_func)
>  		(*error_func)(ecodes[(int)ecode]);
>  	while (ch != EOF && ch != '\n')

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-stable" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010712065159.619703E2F>