We've run into a few errors due to very long variable expansion in bsd.port.mk
that overflows execve(2)
I would consider adding a modifier to give the length of a variable, so
that significant tests can be done, along the lines of
.if ${VARIABLE:len} > 200000
ERRORS += "Fatal: variable may overflow execve"
.endif
Now, this is a pure extension. We already have some highly similar modifier
in the presence of the very old SystemV :sh to exec variables,
and there's no ambiguity in there.
Would that feature be amenable for inclusion ?
(I'm aware I need to test asprintf's result, it's just a POC at the moment)
Index: varmodifiers.c
===================================================================
RCS file: /cvs/src/usr.bin/make/varmodifiers.c,v
retrieving revision 1.48
diff -u -p -r1.48 varmodifiers.c
--- varmodifiers.c 30 Aug 2020 12:16:04 -0000 1.48
+++ varmodifiers.c 22 Aug 2023 16:19:31 -0000
@@ -147,6 +147,8 @@ static void *check_empty(const char **,
static void *check_quote(const char **, SymTable *, bool, int);
static char *do_upper(const char *, const struct Name *, void *);
static char *do_lower(const char *, const struct Name *, void *);
+static void *check_length(const char **, SymTable *, bool, int);
+static char *do_length(const char *, const struct Name *, void *);
static void *check_shcmd(const char **, SymTable *, bool, int);
static char *do_shcmd(const char *, const struct Name *, void *);
static char *do_sort(const char *, const struct Name *, void *);
@@ -204,7 +206,8 @@ static struct modifier {
label_mod = {true, check_empty, do_label, NULL, NULL},
path_mod = {true, check_empty, do_path, NULL, NULL},
assign_mod = {true, assign_get_value, do_assign, NULL, free_patternarg},
- exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg}
+ exec_mod = {true, get_cmd, do_exec, NULL, free_patternarg},
+ length_mod = {false, check_length, do_length, NULL, NULL}
;
void
@@ -219,6 +222,7 @@ VarModifiers_Init()
choose_mod['H'] = &head_mod;
choose_mod['E'] = &suffix_mod;
choose_mod['R'] = &root_mod;
+ choose_mod['l'] = &length_mod;
if (FEATURES(FEATURE_UPPERLOWER)) {
choose_mod['U'] = &upper_mod;
choose_mod['L'] = &lower_mod;
@@ -1202,6 +1206,26 @@ do_lower(const char *s, const struct Nam
for (i = 0; i < len; i++)
t[i] = TOLOWER(s[i]);
t[len] = '\0';
+ return t;
+}
+
+static void *
+check_length(const char **p, SymTable *ctxt UNUSED, bool b UNUSED, int endc)
+{
+ if ((*p)[1] == 'e' && (*p)[2] == 'n' &&
+ ((*p)[3] == endc || (*p)[3] == ':')) {
+ (*p)+=3;
+ return dummy_arg;
+ } else
+ return NULL;
+}
+
+static char *
+do_length(const char *s, const struct Name *n UNUSED, void *arg UNUSED)
+{
+ char *t;
+
+ asprintf(&t, "%zu", strlen(s));
return t;
}