On Wed, May 14, 2025 at 11:57:08AM -0400, Douglas McIlroy wrote:
> This program
>     define(`A', x define('A',y)A z)
>     A
> produces this answer
> 
>     y

Not as written, it doesn't.  Because of your mismatched quoting (you
used ' unpaired to a leading `), your inner call to define is defining
macro "'A'", so the last A in the first line is not yet a macro at
that point in time, and then the outer define creates macro "A" whose
expansion is "x A z".  So attempting to expand A in the second line
causes an infinite loop of printing "x x x ..." (at least until the
machine runs out of memory to store the corresponding tail of "z z z
..." that it is batching up in memory while expanding that loop on
macro A).

But if I understand what you meant,

define(`A', x define(`A', y)A z)

should indeed define macro "A" first to "y", then replace that
definition to "x y z".

And that's the behavior I get in both m4 1.4.19 and 1.4.20:
$ m4 -daeqt
define(`A', x define(`A', y)A z)
m4trace: -2- define(`A', `y')
m4trace: -2- A -> `y'
m4trace: -1- define(`A', `x y z')

A
m4trace: -1- A -> `x y z'
x y z

You're going to have to be more specific about what version of m4 you
are using that sees a bug here, or give me a formula that actually
reproduces the problem.

> 
> It appears that the inner definition stamps on the
> outer definition in progress. I imagine that each
> allocates and fills a string that it assigns as the
> stored definition for A, The inner one, being
> assigned later, wins. The outer one likely
> becomes either a memory leak or a use of
> freed storage.

I would not be surprised if there are older versions of GNU m4 that
had a bug in this area; 1.4.20 explicitly documents:

** Fix regression introduced in 1.4.19 where trace output (such as with
   `debugmode(t)') could read invalid memory when tracing a series of
   pushed macros that are popped during argument collection.

but trace output is different than define definitions, and your
example didn't include trace output.

> 
> I claim the output should be
>     x y z

Yes, that matches my reading of the POSIX requirements on the
corrected spelling of the example you gave.

> 
> I also claim this is not a pathological example
> that should be declared illegal. I ran into it in a
> subtle guise that was completely reasonable.
> 
> If my guess about the nature of the collision is
> correct, the fix would be to make the assignments
> referred to above at the end of the definition
> process, not the beginning.

I also ran your (corrected) example under valgrind, and it did not
detect any obvious use-after-free violations.  I'm not ruling out a
bug, just that I'm not seeing what you are claiming.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org


Reply via email to