[PATCH 20/31] dutil: Add exec_objcopy() shell-injection-safe helper

Arnaldo Carvalho de Melo <[email protected]> Wed, 29 Jul 2026 16:07:20 -0300
Newsgroups org.kernel.vger.dwarves,org.kernel.vger.bpf
Message-ID <[email protected]>
From: Arnaldo Carvalho de Melo <[email protected]>

Add exec_objcopy() that runs objcopy via posix_spawnp() instead of
system(), eliminating shell injection risk from filenames containing
shell metacharacters.

Signal handling follows POSIX system() semantics: SIGINT/SIGQUIT
ignored in parent during wait, SIGCHLD blocked, default dispositions
restored in child.

No callers yet — the next commit converts btf_encoder and libctf.

Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
 dutil.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 dutil.h |  3 +++
 2 files changed, 75 insertions(+)

diff --git a/dutil.c b/dutil.c
index 14f134016add1f0c..38101f184faf58ee 100644
--- a/dutil.c
+++ b/dutil.c
@@ -9,9 +9,15 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <signal.h>
+#include <spawn.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+extern char **environ;
 
 void *zalloc(size_t size)
 {
@@ -234,3 +240,69 @@ char *strlwr(char *s)
 
 	return s;
 }
+
+int exec_objcopy(const char *objcopy, const char *add_section,
+		 const char *filename)
+{
+	sigset_t mask, orig;
+	struct sigaction ign, old_int, old_quit;
+	posix_spawnattr_t attr;
+	sigset_t child_default;
+	pid_t pid;
+	int rc;
+
+	char *argv[] = {
+		(char *)objcopy,
+		"--add-section", (char *)add_section,
+		"--", (char *)filename,
+		NULL
+	};
+
+	/* Ignore SIGINT/SIGQUIT in parent while waiting, per POSIX system() */
+	ign.sa_handler = SIG_IGN;
+	sigemptyset(&ign.sa_mask);
+	ign.sa_flags = 0;
+	sigaction(SIGINT, &ign, &old_int);
+	sigaction(SIGQUIT, &ign, &old_quit);
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGCHLD);
+	pthread_sigmask(SIG_BLOCK, &mask, &orig);
+
+	posix_spawnattr_init(&attr);
+	/* Restore default signal disposition and mask in child */
+	sigemptyset(&child_default);
+	sigaddset(&child_default, SIGINT);
+	sigaddset(&child_default, SIGQUIT);
+	posix_spawnattr_setsigdefault(&attr, &child_default);
+	posix_spawnattr_setsigmask(&attr, &orig);
+	posix_spawnattr_setflags(&attr,
+				 POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK);
+
+	rc = posix_spawnp(&pid, objcopy, NULL, &attr, argv, environ);
+	posix_spawnattr_destroy(&attr);
+
+	if (rc != 0) {
+		sigaction(SIGINT, &old_int, NULL);
+		sigaction(SIGQUIT, &old_quit, NULL);
+		pthread_sigmask(SIG_SETMASK, &orig, NULL);
+		return rc == ENOENT ? -ENOENT : -1;
+	}
+
+	int status;
+
+	while (waitpid(pid, &status, 0) == -1) {
+		if (errno != EINTR) {
+			status = -1;
+			break;
+		}
+	}
+
+	sigaction(SIGINT, &old_int, NULL);
+	sigaction(SIGQUIT, &old_quit, NULL);
+	pthread_sigmask(SIG_SETMASK, &orig, NULL);
+
+	if (status == -1 || !WIFEXITED(status))
+		return -1;
+
+	return WEXITSTATUS(status) == 0 ? 0 : -1;
+}
diff --git a/dutil.h b/dutil.h
index be02c68cf4a7ff22..603556fa03085abf 100644
--- a/dutil.h
+++ b/dutil.h
@@ -351,6 +351,9 @@ static inline int elf_getshdrstrndx(Elf *elf, size_t *dst)
 
 char *strlwr(char *s);
 
+int exec_objcopy(const char *objcopy, const char *add_section,
+		 const char *filename);
+
 void __zfree(void **ptr);
 
 #define zfree(ptr) __zfree((void **)(ptr))
-- 
2.55.0