find: -exec is terminated by + only if the prior arg is exactly '{}'

A "+" only terminates -exec when it immediately follows an argument
which is exactly "{}" (and not, for example, "{}x").  This fixes
Savannah bug 66365.

* NEWS: explain this change.
* doc/find.texi: update one place which omitted the '{}' before '+'.
* find/parser.c (insert_exec_ok): consider + to be special ony if it
  follows an argument which is exactly '{}'.
* tests/find/sv-bug-66365-exec.sh: test for this bug.
* tests/local.mk: add the new test file.
This commit is contained in:
James Youngman
2024-10-31 15:06:02 +00:00
parent 457acfa06b
commit 1dcdf3de8e
5 changed files with 50 additions and 9 deletions

6
NEWS
View File

@@ -7,6 +7,12 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout)
'find -ignore_readdir_race' now has a race between FTS read and the visiting
of the entry when the file was removed. [#45930]
To fix a POSIX compatibility bug, -exec foo Z{} + is no longer a
complete predicate, because '+' is only a terminator when it follows
an argument which is exactly '{}'. The findutils documentation
already states this, and now find's behaviour matches the
documentation.
** Documentation Changes
The forthcoming Issue 8 of the POSIX standard will standardise "find

View File

@@ -1536,7 +1536,7 @@ This is different to @samp{-prune} because @samp{-prune} only applies
to the contents of pruned directories, while @samp{-quit} simply makes
@code{find} stop immediately. No child processes will be left
running. Any command lines which have been built by @samp{-exec
... \+} or @samp{-execdir ... \+} are invoked before the program is
... @{@} +} or @samp{-execdir ... \+} are invoked before the program is
exited. After @samp{-quit} is executed, no more files specified on
the command line will be processed. For example, @samp{find /tmp/foo
/tmp/bar -print -quit} will print only @samp{/tmp/foo}. One common

View File

@@ -2770,7 +2770,7 @@ insert_exec_ok (const char *action,
{
int start, end; /* Indexes in ARGV of start & end of cmd. */
int i; /* Index into cmd args */
int saw_braces; /* True if previous arg was '{}'. */
bool prev_was_braces_only; /* Previous arg was '{}' (not e.g. 'Q' or '{}x'). */
bool allow_plus; /* True if + is a valid terminator */
int brace_count; /* Number of instances of {}. */
const char *brace_arg; /* Which arg did {} appear in? */
@@ -2827,28 +2827,35 @@ insert_exec_ok (const char *action,
* Also figure out if the command is terminated by ";" or by "+".
*/
start = *arg_ptr;
for (end = start, saw_braces=0, brace_count=0, brace_arg=NULL;
for (end = start, prev_was_braces_only=false, brace_count=0, brace_arg=NULL;
(argv[end] != NULL)
&& ((argv[end][0] != ';') || (argv[end][1] != '\0'));
end++)
{
/* For -exec and -execdir, "{} +" can terminate the command. */
if ( allow_plus
&& argv[end][0] == '+' && argv[end][1] == 0
&& saw_braces)
if (allow_plus && prev_was_braces_only
&& argv[end][0] == '+' && argv[end][1] == 0)
{
our_pred->args.exec_vec.multiple = 1;
break;
}
saw_braces = 0;
prev_was_braces_only = false;
if (mbsstr (argv[end], "{}"))
{
saw_braces = 1;
if (0 == strcmp(argv[end], "{}"))
{
/* Savannah bug 66365: + only terminates the predicate
* immediately after an argument which is exactly, "{}".
* However, the "{}" in "x{}" should get expanded for
* the ";" case.
*/
prev_was_braces_only = true;
}
brace_arg = argv[end];
++brace_count;
if (0 == end && (func == pred_execdir || func == pred_okdir))
if (start == end && (func == pred_execdir || func == pred_okdir))
{
/* The POSIX standard says that {} replacement should
* occur even in the utility name. This is insecure

View File

@@ -0,0 +1,27 @@
#!/bin/sh
# Test that find -exec ... + treats the + as a terminator only when it
# immediately follows a {}. See Savannah bug #66365.
# Copyright (C) 2024 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; fu_path_prepend_
print_ver_ find
find . -prune -exec echo x{} + \; >| out
echo 'x. +' >| exp || framework_failure_
compare exp out || fail=1
Exit $fail

View File

@@ -130,6 +130,7 @@ sh_tests = \
tests/find/used.sh \
tests/find/newer.sh \
tests/find/opt-numeric-arg.sh \
tests/find/sv-bug-66365-exec.sh \
tests/find/user-group-max.sh \
tests/xargs/conflicting_opts.sh \
tests/xargs/verbose-quote.sh \