* execute_control_command may not remove its cleanups
@ 2004-02-19 15:32 Dave Allan
2004-02-19 15:40 ` Daniel Jacobowitz
0 siblings, 1 reply; 6+ messages in thread
From: Dave Allan @ 2004-02-19 15:32 UTC (permalink / raw)
To: gdb
Hello,
I ran into a reproducible segfault in gdb (v.5.3, but the offending code
is still present in the CVS tree I checked out this morning). I traced
the problemto the execute_control_command function in cli/cli-script.c.
It appears that execute_control_command doesn't always do or discard the
cleanups it creates before returning, which is not right according to
the gdb internals docs. According to section 13.1 Cleanups, "Your
function should explicitly do or discard the cleanups it creates.
Failing to do this leads to non-deterministic behavior..."
The problem is that the call to do_cleanups in execute_control_command
is conditional (cli/cli-script.c at line 430):
if (old_chain)
do_cleanups (old_chain);
So, if the cleanup_chain was null entering execute_control_command, then
old_chain will be null, and the call to do_cleanups doesn't happen.
Removing the if statement, thus making the do_cleanups (old_chain)
unconditional eliminates the segfault.
Unfortunately, the segfault occurred while I was using gdb run under the
"crash" kernel dump analysis tool, and it appears to me that under
normal gdb usage, cleanup_chain is never null going into
execute_control_command. Thus, do_cleanups is always executed and the
segfault never appears and I don't have a reproducible test case that
works against a vanilla build.
However, it seems from code inspection and the gdb internals
documentation that the call to do_cleanups ought to be unconditional.
Does that seem right?
Dave
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: execute_control_command may not remove its cleanups
2004-02-19 15:32 execute_control_command may not remove its cleanups Dave Allan
@ 2004-02-19 15:40 ` Daniel Jacobowitz
2004-02-19 18:26 ` Dave Allan
0 siblings, 1 reply; 6+ messages in thread
From: Daniel Jacobowitz @ 2004-02-19 15:40 UTC (permalink / raw)
To: Dave Allan; +Cc: gdb
On Thu, Feb 19, 2004 at 10:28:38AM -0500, Dave Allan wrote:
> Hello,
>
> I ran into a reproducible segfault in gdb (v.5.3, but the offending code
> is still present in the CVS tree I checked out this morning). I traced
> the problemto the execute_control_command function in cli/cli-script.c.
>
> It appears that execute_control_command doesn't always do or discard the
> cleanups it creates before returning, which is not right according to
> the gdb internals docs. According to section 13.1 Cleanups, "Your
> function should explicitly do or discard the cleanups it creates.
> Failing to do this leads to non-deterministic behavior..."
>
> The problem is that the call to do_cleanups in execute_control_command
> is conditional (cli/cli-script.c at line 430):
>
> if (old_chain)
> do_cleanups (old_chain);
>
> So, if the cleanup_chain was null entering execute_control_command, then
> old_chain will be null, and the call to do_cleanups doesn't happen.
>
> Removing the if statement, thus making the do_cleanups (old_chain)
> unconditional eliminates the segfault.
>
> Unfortunately, the segfault occurred while I was using gdb run under the
> "crash" kernel dump analysis tool, and it appears to me that under
> normal gdb usage, cleanup_chain is never null going into
> execute_control_command. Thus, do_cleanups is always executed and the
> segfault never appears and I don't have a reproducible test case that
> works against a vanilla build.
>
> However, it seems from code inspection and the gdb internals
> documentation that the call to do_cleanups ought to be unconditional.
> Does that seem right?
No, instead, the cleanup chain should always have an item on it. If
make_cleanup is not called then old_chain will remain NULL, and
do_cleanups (NULL) means "do all cleanups", not "do nothing". It looks
to me like command_handler is responsible for there always being a
cleanup on the chain:
old_chain = make_cleanup (null_cleanup, 0);
but maybe I'm mistaken about that; it's a bit far down the tree.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: execute_control_command may not remove its cleanups
2004-02-19 15:40 ` Daniel Jacobowitz
@ 2004-02-19 18:26 ` Dave Allan
2004-02-19 18:47 ` Daniel Jacobowitz
0 siblings, 1 reply; 6+ messages in thread
From: Dave Allan @ 2004-02-19 18:26 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb
> > However, it seems from code inspection and the gdb internals
> > documentation that the call to do_cleanups ought to be unconditional.
> > Does that seem right?
>
> No, instead, the cleanup chain should always have an item on it. If
> make_cleanup is not called then old_chain will remain NULL, and
> do_cleanups (NULL) means "do all cleanups", not "do nothing". It looks
> to me like command_handler is responsible for there always being a
> cleanup on the chain:
> old_chain = make_cleanup (null_cleanup, 0);
> but maybe I'm mistaken about that; it's a bit far down the tree.
I definitely understand that do_cleanups(NULL) will do all cleanups
which is not what's wanted here. The call is do_cleanups(old_chain),
though, so if there are cleanups on the chain already, they are
preserved. The problem isn't the do_cleanups call, it's the fact that
the do_cleanups call is conditional. The solution is to remove the if
(old_chain) statement and always do the cleanup.
Given what's stated in the docs, that a function must always remove the
cleanups it creates, it would seem to me that regardless of the state of
cleanup_chain at the beginning of execute_control_command, whether it's
NULL or contains cleanups, we want to get back to that state before we
return.
Looking at what cleanups execute_control_command puts on cleanup_chain,
that is correct. Either one or two cleanups are put on the chain where
arg is an automatic variable and function is free_current_contents. If
these cleanups aren't done before the stack frame is destroyed,
something undefined will later be freed when the cleanups are done.
For example (and this output is from debugging the gdb I compiled from
the CVS tree this morning), immediately prior to the call to do_cleanups
at line 431 cleanup_chain contains the following:
$11 = {next = 0x82a3d50, function = 0x8080310 <free_current_contents>,
arg = 0xbffff1f4}
$12 = {next = 0x82cf628, function = 0x8080310 <free_current_contents>,
arg = 0xbffff1f8}
$13 = {next = 0x0, function = 0x8080354 <null_cleanup>, arg = 0x0}
So, in this case, as you point out, we return to one entry on
cleanup_chain, however, it seems unsafe to me to count on there always
being an entry on the chain rather than explicitly removing whatever we
put on the chain, which doesn't affect prior entries.
Dave
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: execute_control_command may not remove its cleanups
2004-02-19 18:26 ` Dave Allan
@ 2004-02-19 18:47 ` Daniel Jacobowitz
2004-02-19 19:14 ` Dave Allan
0 siblings, 1 reply; 6+ messages in thread
From: Daniel Jacobowitz @ 2004-02-19 18:47 UTC (permalink / raw)
To: Dave Allan; +Cc: gdb
On Thu, Feb 19, 2004 at 01:21:52PM -0500, Dave Allan wrote:
> > > However, it seems from code inspection and the gdb internals
> > > documentation that the call to do_cleanups ought to be unconditional.
> > > Does that seem right?
> >
> > No, instead, the cleanup chain should always have an item on it. If
> > make_cleanup is not called then old_chain will remain NULL, and
> > do_cleanups (NULL) means "do all cleanups", not "do nothing". It looks
> > to me like command_handler is responsible for there always being a
> > cleanup on the chain:
> > old_chain = make_cleanup (null_cleanup, 0);
> > but maybe I'm mistaken about that; it's a bit far down the tree.
>
> I definitely understand that do_cleanups(NULL) will do all cleanups
> which is not what's wanted here. The call is do_cleanups(old_chain),
> though, so if there are cleanups on the chain already, they are
> preserved. The problem isn't the do_cleanups call, it's the fact that
> the do_cleanups call is conditional. The solution is to remove the if
> (old_chain) statement and always do the cleanup.
>
> Given what's stated in the docs, that a function must always remove the
> cleanups it creates, it would seem to me that regardless of the state of
> cleanup_chain at the beginning of execute_control_command, whether it's
> NULL or contains cleanups, we want to get back to that state before we
> return.
>
> Looking at what cleanups execute_control_command puts on cleanup_chain,
> that is correct. Either one or two cleanups are put on the chain where
> arg is an automatic variable and function is free_current_contents. If
> these cleanups aren't done before the stack frame is destroyed,
> something undefined will later be freed when the cleanups are done.
Think about this again. Both of those cleanups are conditionally
created. If neither of them is created, old_chain will still be NULL.
This will lead to running cleanups prematurely. If the cleanup chain
is non-empty, things work OK.
The alternative is null_cleanup.
--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: execute_control_command may not remove its cleanups
2004-02-19 18:47 ` Daniel Jacobowitz
@ 2004-02-19 19:14 ` Dave Allan
2004-02-19 22:19 ` Andrew Cagney
0 siblings, 1 reply; 6+ messages in thread
From: Dave Allan @ 2004-02-19 19:14 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb
Ok. I see your point. How about setting old_chain to cleanup_chain
unconditionally at the beginning of the function and doing the cleanups
unconditionally at the end? That way, we're safe against both
scenarios: against doing cleanups prematurely, but also safe against
getting into the function with cleanup_chain null and then freeing
something random at a later point.
Dave
On Thu, 2004-02-19 at 13:47, Daniel Jacobowitz wrote:
> On Thu, Feb 19, 2004 at 01:21:52PM -0500, Dave Allan wrote:
> > > > However, it seems from code inspection and the gdb internals
> > > > documentation that the call to do_cleanups ought to be unconditional.
> > > > Does that seem right?
> > >
> > > No, instead, the cleanup chain should always have an item on it. If
> > > make_cleanup is not called then old_chain will remain NULL, and
> > > do_cleanups (NULL) means "do all cleanups", not "do nothing". It looks
> > > to me like command_handler is responsible for there always being a
> > > cleanup on the chain:
> > > old_chain = make_cleanup (null_cleanup, 0);
> > > but maybe I'm mistaken about that; it's a bit far down the tree.
> >
> > I definitely understand that do_cleanups(NULL) will do all cleanups
> > which is not what's wanted here. The call is do_cleanups(old_chain),
> > though, so if there are cleanups on the chain already, they are
> > preserved. The problem isn't the do_cleanups call, it's the fact that
> > the do_cleanups call is conditional. The solution is to remove the if
> > (old_chain) statement and always do the cleanup.
> >
> > Given what's stated in the docs, that a function must always remove the
> > cleanups it creates, it would seem to me that regardless of the state of
> > cleanup_chain at the beginning of execute_control_command, whether it's
> > NULL or contains cleanups, we want to get back to that state before we
> > return.
> >
> > Looking at what cleanups execute_control_command puts on cleanup_chain,
> > that is correct. Either one or two cleanups are put on the chain where
> > arg is an automatic variable and function is free_current_contents. If
> > these cleanups aren't done before the stack frame is destroyed,
> > something undefined will later be freed when the cleanups are done.
>
> Think about this again. Both of those cleanups are conditionally
> created. If neither of them is created, old_chain will still be NULL.
> This will lead to running cleanups prematurely. If the cleanup chain
> is non-empty, things work OK.
>
> The alternative is null_cleanup.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: execute_control_command may not remove its cleanups
2004-02-19 19:14 ` Dave Allan
@ 2004-02-19 22:19 ` Andrew Cagney
0 siblings, 0 replies; 6+ messages in thread
From: Andrew Cagney @ 2004-02-19 22:19 UTC (permalink / raw)
To: da_gdb; +Cc: Daniel Jacobowitz, gdb
Dave,
> Ok. I see your point. How about setting old_chain to cleanup_chain
> unconditionally at the beginning of the function and doing the cleanups
> unconditionally at the end? That way, we're safe against both
> scenarios: against doing cleanups prematurely, but also safe against
> getting into the function with cleanup_chain null and then freeing
> something random at a later point.
FYI, that is one of the ways recommended in the doco:
> The first style is try/finally. Before it exits, your code-block calls
> @code{do_cleanups} with the old cleanup chain and thus ensures that your
> code-block's cleanups are always performed. For instance, the following
> code-segment avoids a memory leak problem (even when @code{error} is
> called and a forced stack unwind occurs) by ensuring that the
> @code{xfree} will always be called:
>
> @smallexample
> struct cleanup *old = make_cleanup (null_cleanup, 0);
> data = xmalloc (sizeof blah);
> make_cleanup (xfree, data);
> ... blah blah ...
> do_cleanups (old);
> @end smallexample
The main reason why the the code you're studying doesn't match the doco
is because it pre-dates that said doco :-(
Andrew
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2004-02-19 22:19 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-02-19 15:32 execute_control_command may not remove its cleanups Dave Allan
2004-02-19 15:40 ` Daniel Jacobowitz
2004-02-19 18:26 ` Dave Allan
2004-02-19 18:47 ` Daniel Jacobowitz
2004-02-19 19:14 ` Dave Allan
2004-02-19 22:19 ` Andrew Cagney
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox