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