Skip to content

Commit 710f7df

Browse files
committed
gdb: remove the !startup_with_shell path from construct_inferior_arguments
In the commit: commit 0df62bf Date: Fri Oct 22 07:19:33 2021 +0000 gdb: Support some escaping of args with startup-with-shell being off nat/fork-inferior.c was updated such that when we are starting an inferior without a shell we now remove escape characters. The benefits of this are explained in that commit, but having made this change we can now make an additional change. Currently, in construct_inferior_arguments, when startup_with_shell is false we construct the inferior argument string differently than when startup_with_shell is true; when true we apply some escaping to special shell character, when false we don't. This commit simplifies construct_inferior_arguments by removing the !startup_with_shell case, and instead we now apply escaping in all cases. This is fine because, thanks to the above commit the escaping will be correctly removed again when we call into nat/fork-inferior.c. We should think of construct_inferior_arguments and nat/fork-inferior.c as needing to cooperate in order for argument handling to work correctly. construct_inferior_arguments converts a list of separate arguments into a single string, and nat/fork-inferior.c splits that single string back into a list of arguments. It is critical that, if nat/fork-inferior.c is expecting to remove a "layer" of escapes, then construct_inferior_arguments must add that expected "layer", otherwise, we end up stripping more escapes than expected. The great thing (I think) about the new configuration, is that GDB no longer cares about startup_with_shell at the point the arguments are being setup. We only care about startup_with_shell at the point that the inferior is started. This means that a user can set the inferior arguments, and then change the startup-with-shell setting, and GDB will do what they expect. Under the previous system, where construct_inferior_arguments changed its behaviour based on startup_with_shell, the user had to change the setting, and then set the arguments, otherwise, GDB might not do what they expect. There is one slight issue with this commit though, which will be addressed by the next commit. For GDB's native targets construct_inferior_arguments is reached via two code paths; first when GDB starts and we combine arguments from the command line, and second when the Python API is used to set the arguments from a sequence. It's the command line argument handling which we are interested in. Consider this: $ gdb --args /tmp/exec '$FOO' (gdb) show args Argument list to give program being debugged when it is started is "\$FOO". Notice that the argument has become \$FOO, the '$' is now quoted. This is because, by quoting the argument in the shell command that started GDB, GDB was passed a literal $FOO with no quotes. In order to ensure that the inferior sees this same value, GDB added the extra escape character. When GDB starts with a shell we pass \$FOO, which results in the inferior seeing a literal $FOO. But what if the user _actually_ wanted to have the shell GDB uses to start the inferior expand $FOO? Well, it appears this can't be done from the command line, but from the GDB prompt we can just do: (gdb) set args $FOO (gdb) show args Argument list to give program being debugged when it is started is "$FOO". And now the inferior will see the shell expanded version of $FOO. It might seem like we cannot achieve the same result from the GDB command line, however, it is possible with this trick: $ gdb -eiex 'set startup-with-shell off' --args /tmp/exec '$FOO' (gdb) show args Argument list to give program being debugged when it is started is "$FOO". (gdb) show startup-with-shell Use of shell to start subprocesses is off. And now the $FOO is not escaped, but GDB is no longer using a shell to start the inferior, however, we can extend our command line like this: $ gdb -eiex 'set startup-with-shell off' \ -ex 'set startup-with-shell on' \ --args /tmp/exec '$FOO' (gdb) show args Argument list to give program being debugged when it is started is "$FOO". (gdb) show startup-with-shell Use of shell to start subprocesses is on. Use an early-initialisation option to disable startup-with-shell, this is done before command line argument processing, then a normal initialisation option turns startup-with-shell back on after GDB has processed the command line arguments! Is this useful? Yes, absolutely. Is this a good user experience? Absolutely not. And I plan to add a new command line option to GDB (and gdbserver) that will allow users to achieve the same result (this trick doesn't work in gdbserver as there's no early-initialisation there) without having to toggle the startup-with-shell option. The new option can be found in the series here: https://inbox.sourceware.org/gdb-patches/[email protected] The problem is that, that series is pretty long, and getting it reviewed is just not possible. So instead I'm posting the individual patches in smaller blocks, to make reviews easier. So, what's the problem? Well, by removing the !startup_with_shell code path from GDB, there is no longer a construct_inferior_arguments code path that doesn't quote inferior arguments, and so there's no longer a way, from the command line, to set an unquoted '$FOO' as an inferior argument. Obviously, this can still be done from GDB's CLI prompt. The trick above is completely untested, so this regression isn't going to show up in the testsuite. And the breakage is only temporary. In the next commit I'll add a fix which restores the above trick. Of course, I hope that this fix will itself, only be temporary. Once the new command line options that I mentioned above are added, then the fix I add in the next commit can be removed, and user should start using the new command line option. After this commit a whole set of tests that were added as xfail in the above commit are now passing. A change similar to this one can be found in this series: https://inbox.sourceware.org/gdb-patches/[email protected]/ which I reviewed before writing this patch. I don't think there's any one patch in that series that exactly corresponds with this patch though, so I've listed the author of the original series as co-author on this patch. Co-Authored-By: Michael Weghorn <[email protected]> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28392 Tested-By: Guinevere Larsen <[email protected]>
1 parent 5ab7e92 commit 710f7df

File tree

4 files changed

+91
-113
lines changed

4 files changed

+91
-113
lines changed

gdb/testsuite/gdb.base/args.exp

+1-10
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,6 @@ if {[build_executable $testfile.exp $testfile $srcfile] == -1} {
2929
return -1
3030
}
3131

32-
set startup_with_shell_modes { "on" }
33-
if {![gdb_protocol_is_remote]} {
34-
lappend startup_with_shell_modes "off"
35-
} else {
36-
# Some of these tests will not work when using the remote protocol
37-
# due to bug PR gdb/28392.
38-
unsupported "gdbserver 'startup-with-shell off' broken PR gdb/28392"
39-
}
40-
4132
# NAME is the name to use for the tests and ARGLIST is the list of
4233
# arguments that are passed to GDB when it is started.
4334
#
@@ -55,7 +46,7 @@ proc args_test { name arglist {re_list {}} } {
5546
set re_list $arglist
5647
}
5748

58-
foreach_with_prefix startup_with_shell $::startup_with_shell_modes {
49+
foreach_with_prefix startup_with_shell { on off } {
5950
save_vars { ::GDBFLAGS } {
6051
set ::GDBFLAGS "$::GDBFLAGS --args $::binfile $arglist"
6152

gdb/testsuite/gdb.base/inferior-args.exp

+37-11
Original file line numberDiff line numberDiff line change
@@ -174,22 +174,48 @@ set bs "\\\\"
174174
lappend item [list "$hex \"$bs\"\"" "$hex \"$bs$bs$bs\"\""]
175175
lappend test_desc_list $item
176176

177-
set startup_with_shell_modes { "on" }
178-
if {![gdb_protocol_is_remote]} {
179-
lappend startup_with_shell_modes "off"
180-
} else {
181-
# Due to PR gdb/28392 gdbserver doesn't currently support having
182-
# startup-with-shell off, and then attempting to pass arguments
183-
# containing whitespace.
184-
unsupported "bug gdb/28392: gdbserver doesn't support this"
185-
}
186-
177+
# test three
178+
# ----------
179+
#
180+
# This test focuses on sending special shell characters within a
181+
# double quote argument, and each special character is prefixed with a
182+
# backslash.
183+
#
184+
# In a POSIX shell, within a double quoted argument, only $ (dollar),
185+
# ` (backtick), " (double quote), \ (backslash), and newline can be
186+
# escaped. All other backslash characters are literal backslashes.
187+
#
188+
# As with the previous test, the double quotes are lost when the
189+
# arguments are sent through gdbserver_start, as such, this test isn't
190+
# going to work when using the native-gdbserver board, hence we set
191+
# the second arguemnt to 'false'.
192+
lappend test_desc_list [list "test three" \
193+
false \
194+
{ "\&" "\<" "\#" "\^" "\>" "\$" "\`" } \
195+
[list "$hex \"\\\\\\\\&\"" \
196+
"$hex \"\\\\\\\\<\"" \
197+
"$hex \"\\\\\\\\#\"" \
198+
"$hex \"\\\\\\\\\\^\"" \
199+
"$hex \"\\\\\\\\>\"" \
200+
"$hex \"\\\$\"" \
201+
"$hex \"`\""]]
202+
203+
# test four
204+
# ---------
205+
#
206+
# This test passes two arguments, a single and double quote, each
207+
# escaped with a backslash.
208+
lappend test_desc_list [list "test four" \
209+
true \
210+
{ \' \" } \
211+
[list "$hex \"'\"" \
212+
"$hex \"\\\\\"\""]]
187213

188214
foreach desc $test_desc_list {
189215
lassign $desc name stub_suitable args re_list
190216
with_test_prefix $name {
191217
foreach_with_prefix set_method { "start" "starti" "run" "set args" } {
192-
foreach_with_prefix startup_with_shell $startup_with_shell_modes {
218+
foreach_with_prefix startup_with_shell { on off } {
193219
do_test $set_method $startup_with_shell $args $re_list \
194220
$stub_suitable
195221
}

gdb/testsuite/gdb.base/startup-with-shell.exp

+12-25
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,8 @@ proc initial_setup_simple { startup_with_shell run_args } {
5959
# If PROBLEMATIC_ON is true then when startup-with-shell is on we
6060
# expect the comparison to fail, so setup an xfail.
6161
#
62-
# If PROBLEMATIC_OFF is true then when startup-with-shell is off we
63-
# expect the comparison to fail, so setup an xfail.
64-
#
6562
# TESTNAME is a string used in the test names.
66-
proc run_test { args on_re off_re testname { problematic_on false } \
67-
{ problematic_off false } } {
63+
proc run_test { args on_re off_re testname { problematic_on false } } {
6864
foreach startup_with_shell { "on" "off" } {
6965
with_test_prefix "$testname, startup_with_shell: ${startup_with_shell}" {
7066
if {![initial_setup_simple $startup_with_shell $args]} {
@@ -76,7 +72,7 @@ proc run_test { args on_re off_re testname { problematic_on false } \
7672
set problematic $problematic_on
7773
} else {
7874
set re $off_re
79-
set problematic $problematic_off
75+
set problematic false
8076
}
8177

8278
if { $problematic } {
@@ -91,9 +87,8 @@ proc run_test { args on_re off_re testname { problematic_on false } \
9187
# This is like the run_test proc except that RE is used as the
9288
# expected argument regexp when startup-with-shell is both on and off.
9389
# For the other arguments, see run_test.
94-
proc run_test_same { args re testname { problematic_on false } \
95-
{ problematic_off false } } {
96-
run_test $args $re $re $testname $problematic_on $problematic_off
90+
proc run_test_same { args re testname } {
91+
run_test $args $re $re $testname
9792
}
9893

9994
# The regexp to match a single '\' character.
@@ -129,48 +124,40 @@ save_vars { env(TEST) } {
129124

130125
run_test_same "\"\\a\"" \
131126
"\"${bs}${bs}a\"" \
132-
"retain backslash in double quote arg" \
133-
false $is_remote_p
127+
"retain backslash in double quote arg"
134128

135129
run_test_same "'\\a'" \
136130
"\"${bs}${bs}a\"" \
137-
"retain backslash in single quote arg" \
138-
false $is_remote_p
131+
"retain backslash in single quote arg"
139132

140133
run_test_same "\"\\\$\"" \
141134
"\"\\\$\"" \
142135
"'\$' can be escaped in double quote arg"
143136

144137
run_test_same "'\\\$'" \
145138
"\"${bs}${bs}\\\$\"" \
146-
"'\$' is not escaped in single quote arg" \
147-
false $is_remote_p
139+
"'\$' is not escaped in single quote arg"
148140

149141
run_test_same "\"\\`\"" \
150142
"\"\\`\"" \
151143
"'`' can be escaped in double quote arg"
152144

153145
run_test_same "'\\`'" \
154146
"\"${bs}${bs}`\"" \
155-
"'`' is not escaped in single quote arg" \
156-
false $is_remote_p
147+
"'`' is not escaped in single quote arg"
157148

158149
run_test_same "\"\\\"\"" \
159150
"\"${bs}\"\"" \
160-
"'\"' can be escaped in double quote arg" \
161-
false $is_remote_p
151+
"'\"' can be escaped in double quote arg"
162152

163153
run_test_same "'\\\"'" \
164154
"\"${bs}${bs}${bs}\"\"" \
165-
"'\"' is not escaped in single quote arg" \
166-
false $is_remote_p
155+
"'\"' is not escaped in single quote arg"
167156

168157
run_test_same "\"\\\\\"" \
169158
"\"${bs}${bs}\"" \
170-
"'\\' can be escaped in double quote arg" \
171-
false $is_remote_p
159+
"'\\' can be escaped in double quote arg"
172160

173161
run_test_same "'\\\\'" \
174162
"\"${bs}${bs}${bs}${bs}\"" \
175-
"'\\' is not escaped in single quote arg" \
176-
false $is_remote_p
163+
"'\\' is not escaped in single quote arg"

gdbsupport/common-inferior.cc

+41-67
Original file line numberDiff line numberDiff line change
@@ -31,92 +31,66 @@ construct_inferior_arguments (gdb::array_view<char * const> argv)
3131
{
3232
std::string result;
3333

34-
if (startup_with_shell)
35-
{
3634
#ifdef __MINGW32__
37-
/* This holds all the characters considered special to the
38-
Windows shells. */
39-
static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n";
40-
static const char quote = '"';
35+
/* This holds all the characters considered special to the
36+
Windows shells. */
37+
static const char special[] = "\"!&*|[]{}<>?`~^=;, \t\n";
38+
static const char quote = '"';
4139
#else
42-
/* This holds all the characters considered special to the
43-
typical Unix shells. We include `^' because the SunOS
44-
/bin/sh treats it as a synonym for `|'. */
45-
static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n";
46-
static const char quote = '\'';
40+
/* This holds all the characters considered special to the
41+
typical Unix shells. We include `^' because the SunOS
42+
/bin/sh treats it as a synonym for `|'. */
43+
static const char special[] = "\"!#$&*()\\|[]{}<>?'`~^; \t\n";
44+
static const char quote = '\'';
4745
#endif
48-
for (int i = 0; i < argv.size (); ++i)
46+
for (int i = 0; i < argv.size (); ++i)
47+
{
48+
if (i > 0)
49+
result += ' ';
50+
51+
/* Need to handle empty arguments specially. */
52+
if (argv[i][0] == '\0')
4953
{
50-
if (i > 0)
51-
result += ' ';
54+
result += quote;
55+
result += quote;
56+
}
57+
else
58+
{
59+
#ifdef __MINGW32__
60+
bool quoted = false;
5261

53-
/* Need to handle empty arguments specially. */
54-
if (argv[i][0] == '\0')
62+
if (strpbrk (argv[i], special))
5563
{
56-
result += quote;
64+
quoted = true;
5765
result += quote;
5866
}
59-
else
67+
#endif
68+
for (char *cp = argv[i]; *cp != '\0'; ++cp)
6069
{
61-
#ifdef __MINGW32__
62-
bool quoted = false;
63-
64-
if (strpbrk (argv[i], special))
70+
if (*cp == '\n')
6571
{
66-
quoted = true;
72+
/* A newline cannot be quoted with a backslash (it
73+
just disappears), only by putting it inside
74+
quotes. */
75+
result += quote;
76+
result += '\n';
6777
result += quote;
6878
}
69-
#endif
70-
for (char *cp = argv[i]; *cp; ++cp)
79+
else
7180
{
72-
if (*cp == '\n')
73-
{
74-
/* A newline cannot be quoted with a backslash (it
75-
just disappears), only by putting it inside
76-
quotes. */
77-
result += quote;
78-
result += '\n';
79-
result += quote;
80-
}
81-
else
82-
{
8381
#ifdef __MINGW32__
84-
if (*cp == quote)
82+
if (*cp == quote)
8583
#else
86-
if (strchr (special, *cp) != NULL)
84+
if (strchr (special, *cp) != NULL)
8785
#endif
88-
result += '\\';
89-
result += *cp;
90-
}
86+
result += '\\';
87+
result += *cp;
9188
}
89+
}
9290
#ifdef __MINGW32__
93-
if (quoted)
94-
result += quote;
91+
if (quoted)
92+
result += quote;
9593
#endif
96-
}
97-
}
98-
}
99-
else
100-
{
101-
/* In this case we can't handle arguments that contain spaces,
102-
tabs, or newlines -- see breakup_args(). */
103-
for (char *arg : argv)
104-
{
105-
char *cp = strchr (arg, ' ');
106-
if (cp == NULL)
107-
cp = strchr (arg, '\t');
108-
if (cp == NULL)
109-
cp = strchr (arg, '\n');
110-
if (cp != NULL)
111-
error (_("can't handle command-line "
112-
"argument containing whitespace"));
113-
}
114-
115-
for (int i = 0; i < argv.size (); ++i)
116-
{
117-
if (i > 0)
118-
result += " ";
119-
result += argv[i];
12094
}
12195
}
12296

0 commit comments

Comments
 (0)