Skip to content

Commit 1a312af

Browse files
committed
get_execve_info: read /proc/self/fd/N link for pathname
When fexecve(3) is used to execute a command and the kernel doesn't support execveat(2), glibc will use /proc/self/fd/N for the execve(2) pathname. We need to resolve this link to the actual pathname used for intercept and log_subcmds.
1 parent 831a683 commit 1a312af

1 file changed

Lines changed: 23 additions & 8 deletions

File tree

src/exec_ptrace.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -951,9 +951,10 @@ ptrace_write_vec(pid_t pid, struct sudo_ptrace_regs *regs, char **vec,
951951
/*
952952
* Read a link from /proc/PID and store the result in buf.
953953
* Used to read the cwd and exe links in /proc/PID.
954-
* Returns true on success, else false.
954+
* Returns the number of bytes written to buf on success, else -1.
955+
* Note: name and buf _may_ overlap.
955956
*/
956-
static bool
957+
static ssize_t
957958
proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize)
958959
{
959960
ssize_t len;
@@ -962,14 +963,14 @@ proc_read_link(pid_t pid, const char *name, char *buf, size_t bufsize)
962963

963964
len = snprintf(path, sizeof(path), "/proc/%d/%s", (int)pid, name);
964965
if (len > 0 && len < ssizeof(path)) {
965-
len = readlink(path, buf, bufsize - 1);
966-
if (len != -1) {
966+
len = readlink(path, buf, bufsize);
967+
if (len != -1 && (size_t)len != bufsize) {
967968
/* readlink(2) does not add the NUL for us. */
968969
buf[len] = '\0';
969-
debug_return_bool(true);
970+
debug_return_ssize_t(len + 1);
970971
}
971972
}
972-
debug_return_bool(false);
973+
debug_return_ssize_t(-1);
973974
}
974975

975976
/*
@@ -1011,6 +1012,20 @@ get_execve_info(pid_t pid, struct sudo_ptrace_regs *regs, char **pathname_out,
10111012
"unable to read execve pathname for process %d", (int)pid);
10121013
goto bad;
10131014
}
1015+
1016+
/* For fexecve() the path may be in the form /proc/self/fd/N */
1017+
if (strncmp(argbuf, "/proc/self/fd/", 14) == 0) {
1018+
const char *errstr;
1019+
const char *fdstr = argbuf + 14;
1020+
(void)sudo_strtonum(fdstr, 0, INT_MAX, &errstr);
1021+
if (errstr == NULL) {
1022+
/* Rewrite argbuf with link target (if it is one). */
1023+
ssize_t len = proc_read_link(pid, fdstr, argbuf, bufsize);
1024+
if (len != -1)
1025+
nread = len;
1026+
}
1027+
}
1028+
10141029
/* Defer setting pathname until after all reallocations are done. */
10151030
off = (size_t)nread;
10161031
}
@@ -1626,7 +1641,7 @@ ptrace_verify_post_exec(pid_t pid, struct sudo_ptrace_regs *regs,
16261641
}
16271642

16281643
/* Get the executable path. */
1629-
if (!proc_read_link(pid, "exe", pathname, sizeof(pathname))) {
1644+
if (proc_read_link(pid, "exe", pathname, sizeof(pathname)) == -1) {
16301645
/* Missing /proc file system is not a fatal error. */
16311646
sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: unable to read /proc/%d/exe",
16321647
__func__, (int)pid);
@@ -1768,7 +1783,7 @@ ptrace_intercept_execve(pid_t pid, struct intercept_closure *closure)
17681783
}
17691784

17701785
/* Get the current working directory and execve info. */
1771-
if (!proc_read_link(pid, "cwd", cwd, sizeof(cwd)))
1786+
if (proc_read_link(pid, "cwd", cwd, sizeof(cwd)) == -1)
17721787
(void)strlcpy(cwd, "unknown", sizeof(cwd));
17731788
buf = get_execve_info(pid, &regs, &pathname, &argc, &argv,
17741789
&envc, &envp);

0 commit comments

Comments
 (0)