Check for recipe line count overflow before it overflows

awk 'BEGIN {
       print "x:"
       for (i = 0; i < 65536; i++)
         printf "\techo %d\n", i}
    ' | make -f -

Outputs only "make: 'x' is up to date."  Larger values run only the
lines above 65536.  Reported by Paul Eggert <eggert@cs.ucla.edu>.

* src/commands.c (chop_commands): Check the line count before it has
a chance to overflow.  Use size_t for max count so it can't overflow.
Remove stray 'd' in diagnostic.
This commit is contained in:
Paul Smith 2022-10-09 17:17:18 -07:00
parent 2d943d3d2e
commit 9d24d41801

View File

@ -322,14 +322,12 @@ set_file_variables (struct file *file, const char *stem)
void void
chop_commands (struct commands *cmds) chop_commands (struct commands *cmds)
{ {
unsigned int nlines; unsigned short nlines;
unsigned short idx; unsigned short i;
char **lines; char **lines;
/* If we don't have any commands, /* If we don't have any commands, or we already parsed them, never mind. */
or we already parsed them, never mind. */ if (!cmds || cmds->command_lines != NULL)
if (!cmds || cmds->command_lines != 0)
return; return;
/* Chop CMDS->commands up into lines in CMDS->command_lines. */ /* Chop CMDS->commands up into lines in CMDS->command_lines. */
@ -348,25 +346,27 @@ chop_commands (struct commands *cmds)
} }
else else
{ {
const char *p; const char *p = cmds->commands;
size_t max = 5;
nlines = 5; nlines = 0;
lines = xmalloc (nlines * sizeof (char *)); lines = xmalloc (max * sizeof (char *));
idx = 0;
p = cmds->commands;
while (*p != '\0') while (*p != '\0')
{ {
const char *end = p; const char *end = p;
find_end:; find_end:;
end = strchr (end, '\n'); end = strchr (end, '\n');
if (end == 0) if (end == NULL)
end = p + strlen (p); end = p + strlen (p);
else if (end > p && end[-1] == '\\') else if (end > p && end[-1] == '\\')
{ {
int backslash = 1; int backslash = 1;
const char *b; if (end > p + 1)
for (b = end - 2; b >= p && *b == '\\'; --b) {
backslash = !backslash; const char *b;
for (b = end - 2; b >= p && *b == '\\'; --b)
backslash = !backslash;
}
if (backslash) if (backslash)
{ {
++end; ++end;
@ -374,40 +374,36 @@ chop_commands (struct commands *cmds)
} }
} }
if (idx == nlines) if (nlines == USHRT_MAX)
ON (fatal, &cmds->fileinfo,
_("Recipe has too many lines (limit %hu)"), nlines);
if (nlines == max)
{ {
nlines += 2; max += 2;
lines = xrealloc (lines, nlines * sizeof (char *)); lines = xrealloc (lines, max * sizeof (char *));
} }
lines[idx++] = xstrndup (p, (size_t) (end - p));
lines[nlines++] = xstrndup (p, (size_t) (end - p));
p = end; p = end;
if (*p != '\0') if (*p != '\0')
++p; ++p;
} }
if (idx != nlines)
{
nlines = idx;
lines = xrealloc (lines, nlines * sizeof (char *));
}
} }
/* Finally, set the corresponding CMDS->lines_flags elements and the /* Finally, set the corresponding CMDS->lines_flags elements and the
CMDS->any_recurse flag. */ CMDS->any_recurse flag. */
if (nlines > USHRT_MAX) cmds->ncommand_lines = nlines;
ON (fatal, &cmds->fileinfo, _("Recipe has too many lines (%ud)"), nlines);
cmds->ncommand_lines = (unsigned short)nlines;
cmds->command_lines = lines; cmds->command_lines = lines;
cmds->any_recurse = 0; cmds->any_recurse = 0;
cmds->lines_flags = xmalloc (nlines); cmds->lines_flags = xmalloc (nlines);
for (idx = 0; idx < nlines; ++idx) for (i = 0; i < nlines; ++i)
{ {
unsigned char flags = 0; unsigned char flags = 0;
const char *p = lines[idx]; const char *p = lines[i];
while (ISBLANK (*p) || *p == '-' || *p == '@' || *p == '+') while (ISBLANK (*p) || *p == '-' || *p == '@' || *p == '+')
switch (*(p++)) switch (*(p++))
@ -424,12 +420,12 @@ chop_commands (struct commands *cmds)
} }
/* If no explicit '+' was given, look for MAKE variable references. */ /* If no explicit '+' was given, look for MAKE variable references. */
if (!(flags & COMMANDS_RECURSE) if (! ANY_SET (flags, COMMANDS_RECURSE)
&& (strstr (p, "$(MAKE)") != 0 || strstr (p, "${MAKE}") != 0)) && (strstr (p, "$(MAKE)") != 0 || strstr (p, "${MAKE}") != 0))
flags |= COMMANDS_RECURSE; flags |= COMMANDS_RECURSE;
cmds->lines_flags[idx] = flags; cmds->lines_flags[i] = flags;
cmds->any_recurse |= flags & COMMANDS_RECURSE ? 1 : 0; cmds->any_recurse |= ANY_SET (flags, COMMANDS_RECURSE) ? 1 : 0;
} }
} }