[SV 63016] Don't fail exporting to $(shell ...)

The fix for SV 10593 caused recursive expansion errors when exporting
a variable that contains a $(shell ...) invocation.  If we see this
type of recursion, ignore it and expand to the empty string rather
than failing.

* src/variable.h (env_recursion): New global variable.
* src/variable.c (target_environment): If creating the environment
for a $(shell ...) function increment env_recursion.  Remove the
check for expansion in a shell function context.
* src/expand.c (recursively_expand_for_file): Check for recursive
expansion in a $(shell ...) environment context and if present,
show the verbose message and return the empty string.
* tests/scripts/functions/shell: Add a test for this situation.
This commit is contained in:
Paul Smith 2022-09-08 02:25:20 -04:00
parent 1fc13bf576
commit 7d48401707
4 changed files with 40 additions and 19 deletions

View file

@ -18,9 +18,10 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
#include <assert.h>
#include "commands.h"
#include "debug.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "rule.h"
@ -101,6 +102,16 @@ recursively_expand_for_file (struct variable *v, struct file *file)
struct variable_set_list *save = 0;
int set_reading = 0;
/* If we're expanding to put into the environment of a shell function then
ignore any recursion issues. */
if (v->expanding && env_recursion)
{
DB (DB_VERBOSE,
(_("%s:%lu: not recursively expanding %s to export to shell function\n"),
v->fileinfo.filenm, v->fileinfo.lineno, v->name));
return xstrdup ("");
}
/* Don't install a new location if this location is empty.
This can happen for command-line variables, builtin variables, etc. */
saved_varp = expanding_var;

View file

@ -31,12 +31,15 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
#endif
#include "hash.h"
/* Incremented every time we enter target_environment(). */
unsigned long long env_recursion = 0;
/* Incremented every time we add or remove a global variable. */
static unsigned long variable_changenum;
static unsigned long variable_changenum = 0;
/* Chain of all pattern-specific variables. */
static struct pattern_var *pattern_vars;
static struct pattern_var *pattern_vars = NULL;
/* Pointer to the last struct in the pack of a specific size, from 1 to 255.*/
@ -1038,15 +1041,20 @@ target_environment (struct file *file, int recursive)
int found_mflags = 0;
int found_makeflags = 0;
/* If file is NULL we're creating the target environment for $(shell ...)
Remember this so we can just ignore recursion. */
if (!file)
++env_recursion;
/* We need to update makeflags if (a) we're not recurive, (b) jobserver_auth
is enabled, and (c) we need to add invalidation. */
if (!recursive && jobserver_auth)
invalid = jobserver_get_invalid_auth ();
if (file == 0)
set_list = current_variable_set_list;
else
if (file)
set_list = file->variables;
else
set_list = current_variable_set_list;
hash_init (&table, VARIABLE_BUCKETS,
variable_hash_1, variable_hash_2, variable_hash_cmp);
@ -1101,19 +1109,7 @@ target_environment (struct file *file, int recursive)
expand its value. If it came from the environment, it should
go back into the environment unchanged. */
if (v->recursive && v->origin != o_env && v->origin != o_env_override)
{
/* If V is being recursively expanded and this is for a shell
function, just skip it. */
if (v->expanding && file == NULL)
{
DB (DB_VERBOSE,
(_("%s:%lu: Skipping export of %s to shell function due to recursive expansion\n"),
v->fileinfo.filenm, v->fileinfo.lineno, v->name));
continue;
}
value = cp = recursively_expand_for_file (v, file);
}
/* If this is the SHELL variable remember we already added it. */
if (!added_SHELL && streq (v->name, "SHELL"))
@ -1212,6 +1208,9 @@ target_environment (struct file *file, int recursive)
hash_free (&table, 0);
if (!file)
--env_recursion;
return result_0;
}

View file

@ -114,6 +114,7 @@ struct pattern_var
struct variable variable;
};
extern unsigned long long env_recursion;
extern char *variable_buffer;
extern struct variable_set_list *current_variable_set_list;
extern struct variable *default_goal_var;

View file

@ -88,6 +88,16 @@ all: ; @echo $$HI $(bad)
',
'',"hi hi");
# SV 63016: Exported variable that contains a variable containing $(shell...)
run_make_test('
HI = $(shell echo hi)
export bad = $(HI)
.PHONY: all
all:; : $(HI)
',
'',": hi");
if ($port_type ne 'W32') {
# Test shell errors in recipes including offset
# This needs to be ported to Windows, or else Windows error messages