summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2017-10-22 13:08:56 -0400
committerTavian Barnes <tavianator@tavianator.com>2017-10-22 13:08:56 -0400
commita3ed3e8dda52d39d3307a87b848b54606a6f0fed (patch)
tree2c777d93e434689068f6ce78ddcabc13cbc6fd19
parent627ffbe2aafe05371dee9ca872b3da4f16424ad9 (diff)
downloadbfs-a3ed3e8dda52d39d3307a87b848b54606a6f0fed.tar.xz
exec: Apply more headroom to avoid E2BIG
I ran into "argument list too long" errors with a bfs -type f -exec grep pattern '{}' + command, despite the current accounting being pretty careful. Some experimentation showed that an additional 2048 bytes of headroom is always safe. While we're at it, explicitly account for the terminating NULL pointers in argv and environ.
-rw-r--r--exec.c20
1 files changed, 16 insertions, 4 deletions
diff --git a/exec.c b/exec.c
index 4b34ab6..b982f85 100644
--- a/exec.c
+++ b/exec.c
@@ -77,16 +77,21 @@ static size_t bfs_exec_arg_max(const struct bfs_exec *execbuf) {
for (char **envp = environ; *envp; ++envp) {
arg_max -= bfs_exec_arg_size(*envp);
}
+ // Account for the terminating NULL entry
+ arg_max -= sizeof(char *);
bfs_exec_debug(execbuf, "ARG_MAX: %ld remaining after environment variables\n", arg_max);
// Account for the fixed arguments
for (size_t i = 0; i < execbuf->tmpl_argc - 1; ++i) {
arg_max -= bfs_exec_arg_size(execbuf->tmpl_argv[i]);
}
+ // Account for the terminating NULL entry
+ arg_max -= sizeof(char *);
bfs_exec_debug(execbuf, "ARG_MAX: %ld remaining after fixed arguments\n", arg_max);
// POSIX recommends subtracting 2048, for some wiggle room
- arg_max -= 2048;
+ // We subtract 4096 for extra insurance, based on some experimentation
+ arg_max -= 4096;
bfs_exec_debug(execbuf, "ARG_MAX: %ld remaining after headroom\n", arg_max);
if (arg_max < 0) {
@@ -319,7 +324,12 @@ static int bfs_exec_spawn(const struct bfs_exec *execbuf) {
}
}
- bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments]\n", execbuf->argv[0], execbuf->argc - 1);
+ if (execbuf->flags & BFS_EXEC_MULTI) {
+ bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments] (size %zu)\n",
+ execbuf->argv[0], execbuf->argc - 1, execbuf->arg_size);
+ } else {
+ bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments]\n", execbuf->argv[0], execbuf->argc - 1);
+ }
pid_t pid = fork();
@@ -432,8 +442,10 @@ static bool bfs_exec_needs_flush(struct bfs_exec *execbuf, const struct BFTW *ft
}
}
- if (execbuf->arg_size + bfs_exec_arg_size(arg) > execbuf->arg_max) {
- bfs_exec_debug(execbuf, "Reached max command size, executing buffered command\n");
+ size_t next_size = execbuf->arg_size + bfs_exec_arg_size(arg);
+ if (next_size > execbuf->arg_max) {
+ bfs_exec_debug(execbuf, "Command size (%zu) would exceed maximum (%zu), executing buffered command\n",
+ next_size, execbuf->arg_max);
return true;
}