Fix multiply defined local macros escaping scope
freeArgs() only popped any local macros once, so if a local macro was pushed multiple times, whether through %define or multiple identical options getting passed, we leaked any remaining macros to the outside scope. Simply pop the local macros in a loop to fix. Have the internal popMacro() return the previous pointer (if any) to simplify the job. We even had an expected-fail test for this, now passing, but add a more straightforward reproducer too. This bug was circa 26 years old. Some might call it vintage at this point. Fixes: #3056
This commit is contained in:
parent
e6d6b49f2f
commit
695b5c2521
|
@ -139,7 +139,7 @@ static int expandMacro(rpmMacroBuf mb, const char *src, size_t slen);
|
|||
static int expandQuotedMacro(rpmMacroBuf mb, const char *src);
|
||||
static void pushMacro(rpmMacroContext mc,
|
||||
const char * n, const char * o, const char * b, int level, int flags);
|
||||
static void popMacro(rpmMacroContext mc, const char * n);
|
||||
static rpmMacroEntry popMacro(rpmMacroContext mc, const char * n);
|
||||
static int loadMacroFile(rpmMacroContext mc, const char * fn);
|
||||
/* =============================================================== */
|
||||
|
||||
|
@ -948,10 +948,13 @@ freeArgs(rpmMacroBuf mb)
|
|||
me->flags |= ME_USED;
|
||||
}
|
||||
|
||||
/* compensate if the slot is to go away */
|
||||
if (me->prev == NULL)
|
||||
while ((me = popMacro(mc, me->name))) {
|
||||
if (me->level < mb->level)
|
||||
break;
|
||||
}
|
||||
/* compensate if the slot went away */
|
||||
if (me == NULL)
|
||||
i--;
|
||||
popMacro(mc, me->name);
|
||||
}
|
||||
mb->level--;
|
||||
mb->args = NULL;
|
||||
|
@ -1867,17 +1870,18 @@ static void pushMacro(rpmMacroContext mc,
|
|||
return pushMacroAny(mc, n, o, b, NULL, NULL, 0, level, flags);
|
||||
}
|
||||
|
||||
static void popMacro(rpmMacroContext mc, const char * n)
|
||||
/* Return pointer to the _previous_ macro definition (or NULL) */
|
||||
static rpmMacroEntry popMacro(rpmMacroContext mc, const char * n)
|
||||
{
|
||||
size_t pos;
|
||||
rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
|
||||
if (mep == NULL)
|
||||
return;
|
||||
return NULL;
|
||||
/* parting entry */
|
||||
rpmMacroEntry me = *mep;
|
||||
assert(me);
|
||||
/* detach/pop definition */
|
||||
mc->tab[pos] = me->prev;
|
||||
rpmMacroEntry prev = mc->tab[pos] = me->prev;
|
||||
/* shrink macro table */
|
||||
if (me->prev == NULL) {
|
||||
mc->n--;
|
||||
|
@ -1889,6 +1893,7 @@ static void popMacro(rpmMacroContext mc, const char * n)
|
|||
}
|
||||
/* comes in a single chunk */
|
||||
free(me);
|
||||
return prev;
|
||||
}
|
||||
|
||||
static int defineMacro(rpmMacroContext mc, const char * macro, int level)
|
||||
|
|
|
@ -1087,7 +1087,6 @@ RPMTEST_CLEANUP
|
|||
AT_SETUP([%define + %undefine in nested levels 3])
|
||||
AT_KEYWORDS([macros define])
|
||||
RPMTEST_CHECK([
|
||||
AT_XFAIL_IF([test $RPM_XFAIL -ne 0])
|
||||
# %define macro twice in a nested scope
|
||||
runroot rpm \
|
||||
--define '%foo() %{expand:%define xxx 1} %{echo:%xxx} %{expand: %define xxx 2} %{echo:%xxx}' \
|
||||
|
@ -1100,6 +1099,15 @@ runroot rpm \
|
|||
. .
|
||||
%xxx
|
||||
])
|
||||
|
||||
RPMTEST_CHECK([
|
||||
runroot rpm --define "aa 0" --define "my() %{define:aa 1}%{define:aa 2}" --eval "%my" --eval "%aa"
|
||||
],
|
||||
[0],
|
||||
[
|
||||
0
|
||||
],
|
||||
[ignore])
|
||||
RPMTEST_CLEANUP
|
||||
|
||||
AT_SETUP([%define + %undefine in nested levels 4])
|
||||
|
|
Loading…
Reference in New Issue