[Rpm-maint] [PATCH] Check for undefined macros

Panu Matilainen pmatilai at redhat.com
Fri Feb 15 09:59:00 UTC 2013

On 01/27/2013 07:27 AM, Alexey Tourbin wrote:
> This change introduces a generalized routine rpmExpandMacros() to expand
> macros on behalf of user input, with improved diagnostic facilities.
> In particular, one of its arguments is a callback function which is
> called whenever an undefined macro is encountered in user input.
> When specfile parser calls this routine, it supplies a custom function
> to handle undefined macros.  This function is keenly aware of specfile
> syntax: valid specfile tokens, such as %prep and %setup, when found in
> the right context, are not treated as undefined macros.  Otherwise,
> there are 3 possibilities.
> - Undefined macros in preamble and %pre/%post/... scriptlets raise
>    an error, unless a non-build parse is performed; this is to prevent
>    malformed packages from being built.
> - In other cases, a warning is issued.
> - Some tokens are permitted beyond their context (e.g. %ghost in
>    %changelog), to comply with traditional/sloppy usage.  Also, undefined
>    macros found in comments normally do not produce a warning.

Trying to finally say something meaningful about this...

First of all, I certainly do like the direction this is going to. I've 
been thinking about adding some way to register the spec keywords and 
pseudo-macros into the macro parser but never got around to do anything 
beyond thinking level.

You can probably guess by now what I'm going to say first: this needs 
splitting up :) See below...

> -int expandMacros(void * spec, rpmMacroContext mc, char * sbuf, size_t slen)
> -{
> -    char *target = NULL;
> -    int rc = doExpandMacros(mc, sbuf, &target);
> -    rstrlcpy(sbuf, target, slen);
> -    free(target);
> -    return rc;
> +    free(mb.buf);
> +    return NULL;
>   }

Please leave expandMacros() around for the time being, as a similar 
dummy wrapper as it is now. It needs to go eventually, but removing it 
would force a soname bump and we really dont want to do that just for 
this silly little thing. Keeping it also allows trivially splitting this 
huge commit up a bit: one patch to change the macro side, another to 
change the build code to take advantage of the new interface instead of 
expandMacros(). After that, expandMacros() can be marked for axing on 
the next soname bump, whenever that happens.

> +char * rpmExpandMacros	(rpmMacroContext mc, const char *src,
> +				const char *fileName, int lineNum,
> +				void undefined(const char *fileName, int lineNum,
> +					const char *s, const char *f, const char *fe,
> +					const char *exp, int level, void *arg),
> +				void *arg);

Eek :) This is quite an argument monster for a public API function. Most 
of it is of course actually for the undefined() callback, and could be 
hidden behind a typedef to make it less scary-looking and to require 
less arguments for common uses - I'm basically shopping for something 
that could nicely replace many uses of rpmExpand(), see below. Haven't 
looked in detail how feasible (or not) these would be in practise but 
some ideas / thoughts:

The callback could perhaps be further hidden away in the API with 
setCallback() style function. Even if not, I think it'd make sense to 
lump the callback arguments into a "callback data" struct where 
fileName, lineNum and arg (for caller private data) are passed along so 
you dont need so many individual arguments for the function itself and 
the callback.

On a related note, since fileName and lineNum are really only used for 
error reporting, perhaps it should instead have a retval argument for 
returning the error message string, and leave reporting of file names & 
line numbers to the caller.

Then there's the idea of actually being able to "register" new built-in 
macros into the macro engine. Looking at undefined() in parseSpec.c, 
that's likely not possible to handle by registering spec keywords as 
macros with a custom callback for each case, but OTOH the %files perhaps 
could. But perhaps the bigger advantage would be allowing eg %setup to 
be a real built-in macro instead of the pseudo-macro-foobar it is now.

Finally (although I've a feeling I'm forgetting something here but this 
getting a bit long as it is...), while we're adding a new enhanced macro 
expansion function, I'd like to see a "flags" argument included to allow 
controlling some behaviors, even if unused right now (I'm certainly not 
expecting you to implement any of this, just a brain-dump of what kinds 
of things I have in mind :)

Some possible (future) flags could be:
- a "strict" flag where warnings become errors (vague I know, but 
remember these are just ideas :)
- "readonly" flag where attempt to define or undefine new macros is an 
error / warning and wont be honored
- "noexec" flag where attempt to run shell/lua code is an error

The latter two are kinda security oriented things: rpmExpand() is used 
for grabbing all sorts of configuration settings deep in transactions 
while running with "god powers" (root, unrestricted in selinux etc). 
Those configuration macros are expected to be "simple" things like 
constants or just %{_bindir} -> /usr/bin style expansions, but nothing 
restricts them from executing shell/lua and doing nasty things (whether 
accidentally or on evil purpose).

	- Panu -

More information about the Rpm-maint mailing list