1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
From: Mike Crowe <mac@mcrowe.com>
Date: Fri, 6 Nov 2020 15:22:37 +0000
Subject: Fix large command line on POSIX systems
When presented with a very long command line (as is common when linking
a large number of files with absolute paths in a deep subdirectory),
make fails to execute the command as it doesn't split the command line
to fit within the limits.
This is based on a fix for Debian bug 688601[1] by Adam Conrad applied
by Manoj Srivastava originally applied for Debian make-dfsg 4.0-5 in
2014 but dropped in 2018 (it seems under the incorrect assumption that
it had been accepted upstream.)
I've tweaked Adam's original patch so that it compiles successfully with
-Werror on top of current master. This required:
* moving the eval_line declaration to the top of the block, so I moved
the macros too
* using a const variable when iterating over the shell
* adding a cast to avoid a signed/unsigned mismatch.
As suggested in the Savannah bug report[2], I've added a test case that
fails without the rest of the patch. I'm not sure what the consequences
of running the test on non-POSIX targets would be and whether it needs
marking as an expected failure.
* src/job.c (construct_command_argv_internal): support running commands
longer than MAX_ARG_STRLEN
* tests/scripts/features/long_command_line: add test for such a command
* configure.ac: check for now-required sys/user.h and linux/binfmts.h
headers
[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688601
[2] https://savannah.gnu.org/bugs/?45763#comment2
Bug: https://savannah.gnu.org/bugs/?45763
Last-Update: 2023-02-13
---
configure.ac | 2 +-
src/job.c | 52 +++++++++++++++++++++++++++++++-
tests/scripts/features/long_command_line | 30 ++++++++++++++++++
3 files changed, 82 insertions(+), 2 deletions(-)
create mode 100644 tests/scripts/features/long_command_line
--- a/configure.ac
+++ b/configure.ac
@@ -68,7 +68,7 @@
AC_CHECK_HEADERS([stdlib.h string.h strings.h locale.h unistd.h limits.h \
memory.h sys/param.h sys/resource.h sys/timeb.h sys/time.h \
- sys/select.h sys/file.h fcntl.h spawn.h])
+ sys/select.h sys/file.h fcntl.h spawn.h sys/user.h linux/binfmts.h])
AM_PROG_CC_C_O
AC_C_CONST
--- a/src/job.c
+++ b/src/job.c
@@ -28,6 +28,14 @@
#include "dep.h"
#include "shuffle.h"
+#if defined (HAVE_LINUX_BINFMTS_H) && defined (HAVE_SYS_USER_H)
+#include <sys/user.h>
+#include <linux/binfmts.h>
+#endif
+#ifndef PAGE_SIZE
+# define PAGE_SIZE (sysconf(_SC_PAGESIZE))
+#endif
+
/* Default shell to use. */
#ifdef WINDOWS32
# include <windows.h>
@@ -3274,6 +3282,15 @@
#ifdef WINDOWS32
char *command_ptr = NULL; /* used for batch_mode_shell mode */
#endif
+ char *args_ptr;
+#ifdef MAX_ARG_STRLEN
+ static char eval_line[] = "eval\\ \\\"set\\ x\\;\\ shift\\;\\ ";
+#define ARG_NUMBER_DIGITS 5
+#define EVAL_LEN (sizeof(eval_line)-1 + shell_len + 4 \
+ + (7 + ARG_NUMBER_DIGITS) * 2 * line_len / (MAX_ARG_STRLEN - 2))
+#else
+#define EVAL_LEN 0
+#endif
# ifdef __EMX__ /* is this necessary? */
if (!unixy_shell && shellflags)
@@ -3465,7 +3482,7 @@
}
new_line = xmalloc ((shell_len*2) + 1 + sflags_len + 1
- + (line_len*2) + 1);
+ + (line_len*2) + 1 + EVAL_LEN);
ap = new_line;
/* Copy SHELL, escaping any characters special to the shell. If
we don't escape them, construct_command_argv_internal will
@@ -3486,6 +3503,31 @@
#ifdef WINDOWS32
command_ptr = ap;
#endif
+
+#if !defined (WINDOWS32) && defined (MAX_ARG_STRLEN)
+ if (unixy_shell && line_len > MAX_ARG_STRLEN)
+ {
+ const char *q;
+ unsigned j;
+ memcpy (ap, eval_line, sizeof (eval_line) - 1);
+ ap += sizeof (eval_line) - 1;
+ for (j = 1; j <= 2 * line_len / (MAX_ARG_STRLEN - 2); j++)
+ ap += sprintf (ap, "\\$\\{%u\\}", j);
+ *ap++ = '\\';
+ *ap++ = '"';
+ *ap++ = ' ';
+ /* Copy only the first word of SHELL to $0. */
+ for (q = shell; *q != '\0'; ++q)
+ {
+ if (isspace ((unsigned char)*q))
+ break;
+ *ap++ = *q;
+ }
+ *ap++ = ' ';
+ }
+#endif
+ args_ptr = ap;
+
for (p = line; *p != '\0'; ++p)
{
if (restp != NULL && *p == '\n')
@@ -3532,6 +3574,14 @@
}
#endif
*ap++ = *p;
+#if !defined (WINDOWS32) && defined (MAX_ARG_STRLEN)
+ if (unixy_shell && line_len > MAX_ARG_STRLEN
+ && (ap - args_ptr > (long)(MAX_ARG_STRLEN - 2)))
+ {
+ *ap++ = ' ';
+ args_ptr = ap;
+ }
+#endif
}
if (ap == new_line + shell_len + sflags_len + 2)
{
--- /dev/null
+++ b/tests/scripts/features/long_command_line
@@ -0,0 +1,30 @@
+# -*-perl-*-
+$description = "Test long command line.";
+
+$details = "";
+
+# Variable names containing UTF8 characters
+run_make_test(q!
+# 49 characters
+ARGS:=one two three four five six seven eight niner ten
+# 49*4+3 = 199 characters
+ARGS:=$(ARGS) $(ARGS) $(ARGS) $(ARGS)
+# 199*4+3 = 799 characters
+ARGS:=$(ARGS) $(ARGS) $(ARGS) $(ARGS)
+# 799*4+3 = 3199 characters
+ARGS:=$(ARGS) $(ARGS) $(ARGS) $(ARGS)
+# 3199*4+3 = 12799 characters
+ARGS:=$(ARGS) $(ARGS) $(ARGS) $(ARGS)
+# 12799*4+3 = 51199 characters
+ARGS:=$(ARGS) $(ARGS) $(ARGS) $(ARGS)
+# 51199*4+3 = 204799 characters
+ARGS:=$(ARGS) $(ARGS) $(ARGS) $(ARGS)
+# 24799*2+1 = 409599 characters
+#ARGS:=$(ARGS) $(ARGS)
+
+test:
+ @: $(ARGS)
+!,
+ '', "");
+
+1;
|