m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] symtab: Fix memory corruption when tracing a popdef'd macro


From: Eric Blake
Subject: [PATCH] symtab: Fix memory corruption when tracing a popdef'd macro
Date: Fri, 13 Jan 2023 12:37:20 -0600

While debugging a script with 'm4 -daeqt', I was surprised to see
uninitialized memory in the trace for one of the macro invocations.
Rerunning it under valgrind confirmed a use-after-free.

* src/symtab.c (free_symbol): When popdef marks an in-expansion nested
definition unused, clone its name in case it is being traced.
(lookup_symbol) [SYMBOL_INSERT]: Consistently use SYMBOL_NAME(), and
share the name across a symbol stack instead of needing to clone.
* doc/m4.texi (Undefine): Test this.
* NEWS: Document the fix.

Fixes: ee427b83b5 ("symtab: use less memory in pushdef stacks")
---
 NEWS         |  4 ++++
 doc/m4.texi  | 15 +++++++++++++++
 src/symtab.c | 13 +++++++++----
 3 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index ee443ca5..73db6309 100644
--- a/NEWS
+++ b/NEWS
@@ -5,4 +5,8 @@ GNU M4 NEWS - User visible changes.
 ** The `syscmd' and `esyscmd' builtins no longer mishandle a command line
    starting with `-' or `+'.

+** 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.
+
 * Noteworthy changes in release 1.4.19 (2021-05-28) [stable]

 ** A number of portability improvements inherited from gnulib, including
diff --git a/doc/m4.texi b/doc/m4.texi
index 99c248be..65f701a6 100644
--- a/doc/m4.texi
+++ b/doc/m4.texi
@@ -2227,6 +2227,21 @@ Undefine
 @result{}f(bye)
 @end example

+@ignore
+@comment This example is not worth putting in the manual, but triggers a
+@comment memory corruption regression during tracing in 1.4.19.
+
+@example
+define(`a', `popdef(`a')1')pushdef(`a', `2$*')dnl
+debugmode(`t')a(popdef(`a')a)
+@error{}m4trace: -2- popdef
+@error{}m4trace: -2- a
+@error{}m4trace: -2- popdef
+@error{}m4trace: -1- a
+@result{}21
+@end example
+@end ignore
+
 It is not an error for @var{name} to have no macro definition.  In that
 case, @code{undefine} does nothing.

diff --git a/src/symtab.c b/src/symtab.c
index 742f192b..766eed69 100644
--- a/src/symtab.c
+++ b/src/symtab.c
@@ -141,7 +141,13 @@ void
 free_symbol (symbol *sym)
 {
   if (SYMBOL_PENDING_EXPANSIONS (sym) > 0)
-    SYMBOL_DELETED (sym) = true;
+    {
+      SYMBOL_DELETED (sym) = true;
+      if (SYMBOL_STACK (sym)) {
+        SYMBOL_NAME (sym) = xstrdup (SYMBOL_NAME (sym));
+        SYMBOL_STACK (sym) = NULL;
+      }
+    }
   else
     {
       if (SYMBOL_STACK (sym) == NULL)
@@ -220,15 +226,14 @@ lookup_symbol (const char *name, symbol_lookup mode)
               SYMBOL_TYPE (sym) = TOKEN_VOID;
               SYMBOL_TRACED (sym) = SYMBOL_TRACED (old);
               sym->hash = h;
-              SYMBOL_NAME (sym) = old->name;
-              old->name = xstrdup (name);
+              SYMBOL_NAME (sym) = SYMBOL_NAME (old);
               SYMBOL_MACRO_ARGS (sym) = false;
               SYMBOL_BLIND_NO_ARGS (sym) = false;
               SYMBOL_DELETED (sym) = false;
               SYMBOL_PENDING_EXPANSIONS (sym) = 0;

               SYMBOL_STACK (sym) = SYMBOL_STACK (old);
-              SYMBOL_STACK (old) = NULL;
+              SYMBOL_STACK (old) = sym;
               sym->next = old->next;
               old->next = NULL;
               *spp = sym;
-- 
2.39.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]