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; }