Beware xargs security holes
Chip Salzenberg
chip at tct.uucp
Sat Oct 13 09:37:57 AEST 1990
According to lml at cbnews.att.com (L. Mark Larsen):
>I never much cared for xargs since it limits you to an argument list of
>only 470 bytes.
For the most common use of xargs -- "find ... | xargs command" -- the
script below, called "many", does a good job. Since it doesn't spawn
a subshell, it isn't prone to metacharacter-caused security problems.
And you can configure the max arg size to whatever you like.
Enjoy.
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: many
# Wrapped by chip at tct on Fri Oct 12 19:36:53 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'many' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'many'\"
else
echo shar: Extracting \"'many'\" \(2681 characters\)
sed "s/^X//" >'many' <<'END_OF_FILE'
Xeval 'exec /bin/perl -S $0 ${1+"$@"}'
X if 0;
X
X# $Id: many,v 1.1 90/10/11 13:51:17 chip Exp $
X#
X# Execute the given command with many arguments, one per line.
X# This is like xargs, but it's safer, more flexible and free of
X# the stupid 470-byte argument limit.
X#
X
Xrequire 'signal.ph';
Xrequire 'errno.ph';
X
X$NCARGS = 5120; ## I've never seen it less than this
X
X($ME = $0) =~ s#^.*/##;
X
X$opt_chdir = 0;
X$opt_trace = 0;
X$opt_argfile = undef;
Xwhile (@ARGV) {
X $_ = $ARGV[0];
X last unless s/^-//;
X shift;
X last if $_ eq "-";
X while ($_ ne "") {
X if (s/^d//) { $opt_chdir = 1; }
X elsif (s/^t//) { $opt_trace = 1; }
X elsif (s/^f//) { $opt_argfile = length($_) ? $_ : shift; }
X else { &USAGE; }
X }
X}
X
Xsub USAGE {
X print STDERR <<EOF;
X
Xusage: $ME [-d][-t][-f argfile] command
X
XCollect arguments, one per line, up to maximum argument length,
Xexecuting the given command with those arguments as many times
Xas required until arguments are exhausted.
X
XOptions:
X -d Change to files' directory before spawning command.
X -t Trace mode: print directories and commands before acting.
X -f file Get arguments from specified file instead of standard input.
X
XEOF
X exit 1;
X}
X
X&USAGE unless @ARGV;
X at cmd = @ARGV;
X$cmd_len = 0;
Xgrep($cmd_len += length($_) + 1, @cmd);
X
Xif (defined($opt_argfile)) {
X die "$ME: can't open $opt_argfile: $!\n"
X unless open(ARG, "< $opt_argfile");
X}
Xelse {
X open(ARG, "<&STDIN");
X close(STDIN);
X open(STDIN, "/dev/null") || die "$ME: can't open /dev/null: $!\n";
X}
X
X at args = ();
X$args_len = 0;
Xwhile (<ARG>) {
X chop($a = $_) if /\n$/;
X $a_len = length($a) + 1;
X $doit = ($cmd_len + $args_len + $a_len > $NCARGS);
X if ($opt_chdir) {
X if ($a =~ m#^(.*)/+([^/]+/*)$#) {
X $a_dir = $1;
X $a = $2;
X }
X else {
X $a_dir = ".";
X }
X $spawn_dir = $a_dir unless defined($spawn_dir);
X $doit = 1 if $spawn_dir ne $a_dir;
X }
X if ($doit) {
X &SPAWN(@cmd, @args);
X @args = ();
X $args_len = 0;
X if ($opt_chdir) {
X $spawn_dir = $a_dir;
X }
X }
X if ($cmd_len + $a_len > $NCARGS) {
X print STDERR "$ME: line $.: argument too long\n";
X next;
X }
X push(@args, $a);
X $args_len += $a_len;
X}
X
X&SPAWN(@cmd, @args) if @args;
X
Xexit;
X
Xsub SPAWN {
X local(@av) = @_;
X
X $parent_pid = $$;
X die "$ME: can't fork" unless defined($pid = fork);
X if ($pid == 0) {
X $failed = 0;
X if ($opt_chdir) {
X print STDERR "++ chdir $spawn_dir\n" if $opt_trace;
X unless (chdir $spawn_dir) {
X print STDERR "$ME: $spawn_dir: $!\n";
X $failed = 1;
X }
X }
X unless ($failed) {
X print STDERR join(" ", "+", @cmd, @args), "\n"
X if $opt_trace;
X exec @cmd, @args;
X print STDERR "$ME: $cmd[0]: $!\n";
X }
X kill &SIGTERM, $parent_pid;
X exit 1;
X }
X 0 while wait == -1 && $! == &EINTR;
X 1;
X}
END_OF_FILE
if test 2681 -ne `wc -c <'many'`; then
echo shar: \"'many'\" unpacked with wrong size!
fi
chmod +x 'many'
# end of 'many'
fi
echo shar: End of shell archive.
exit 0
--
Chip Salzenberg at Teltronics/TCT <chip at tct.uucp>, <uunet!pdn!tct!chip>
"I've been cranky ever since my comp.unix.wizards was removed
by that evil Chip Salzenberg." -- John F. Haugh II
More information about the Comp.unix.shell
mailing list