Skip to content

Commit 26c912e

Browse files
authored
Merge pull request #41 from sysprog21/fix-proc
Fix /proc dispatch for host-passthrough dirfds
2 parents 00008a3 + 0000a3b commit 26c912e

File tree

5 files changed

+270
-84
lines changed

5 files changed

+270
-84
lines changed

include/kbox/path.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ bool kbox_is_tty_like_path(const char *path);
3737
/* Check if path is a loader/runtime path (ld.so.cache, /lib, etc.). */
3838
bool kbox_is_loader_runtime_path(const char *path);
3939

40+
/* Check whether a relative path contains ".." as a standalone component. */
41+
bool kbox_relative_path_has_dotdot(const char *path);
42+
43+
/* Check if a relative lookup under a /proc-based host dirfd would resolve
44+
* through a magic proc escape entry.
45+
*
46+
* This covers lookups rooted at /proc, /proc/self, /proc/thread-self, or
47+
* /proc/<pid>, including task/<tid>/... relative paths under per-thread views.
48+
*/
49+
bool kbox_relative_proc_escape_path(const char *path);
50+
4051
/* Normalize path relative to base, resolving . and .. lexically.
4152
* If @input is absolute, @base is ignored. Result is written to @out (must be
4253
* KBOX_MAX_PATH bytes).

src/dispatch-misc.c

Lines changed: 21 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,10 @@ struct kbox_dispatch forward_readlinkat(const struct kbox_syscall_request *req,
108108
struct kbox_supervisor_ctx *ctx)
109109
{
110110
pid_t pid = kbox_syscall_request_pid(req);
111-
long dirfd_raw = to_dirfd_arg(kbox_syscall_request_arg(req, 0));
112-
char pathbuf[KBOX_MAX_PATH];
113-
int rc = guest_mem_read_string(ctx, pid, kbox_syscall_request_arg(req, 1),
114-
pathbuf, sizeof(pathbuf));
111+
char translated[KBOX_MAX_PATH];
112+
long lkl_dirfd;
113+
int rc = translate_request_at_path(req, ctx, 0, 1, translated,
114+
sizeof(translated), &lkl_dirfd);
115115
if (rc < 0)
116116
return kbox_dispatch_errno(-rc);
117117

@@ -123,15 +123,7 @@ struct kbox_dispatch forward_readlinkat(const struct kbox_syscall_request *req,
123123

124124
if (remote_buf == 0)
125125
return kbox_dispatch_errno(EFAULT);
126-
127-
char translated[KBOX_MAX_PATH];
128-
rc = kbox_translate_path_for_lkl(pid, pathbuf, ctx->host_root, translated,
129-
sizeof(translated));
130-
if (rc < 0)
131-
return kbox_dispatch_errno(-rc);
132-
133-
long lkl_dirfd = resolve_open_dirfd(translated, dirfd_raw, ctx->fd_table);
134-
if (lkl_dirfd < 0 && lkl_dirfd != AT_FDCWD_LINUX)
126+
if (should_continue_for_dirfd(lkl_dirfd))
135127
return kbox_dispatch_continue();
136128

137129
if (bufsiz > KBOX_MAX_PATH)
@@ -724,29 +716,20 @@ struct kbox_dispatch forward_symlinkat(const struct kbox_syscall_request *req,
724716
{
725717
pid_t pid = kbox_syscall_request_pid(req);
726718
char targetbuf[KBOX_MAX_PATH];
727-
char linkpathbuf[KBOX_MAX_PATH];
728719
int rc;
729720

730721
rc = guest_mem_read_string(ctx, pid, kbox_syscall_request_arg(req, 0),
731722
targetbuf, sizeof(targetbuf));
732723
if (rc < 0)
733724
return kbox_dispatch_errno(-rc);
734725

735-
long newdirfd_raw = to_dirfd_arg(kbox_syscall_request_arg(req, 1));
736-
737-
rc = guest_mem_read_string(ctx, pid, kbox_syscall_request_arg(req, 2),
738-
linkpathbuf, sizeof(linkpathbuf));
739-
if (rc < 0)
740-
return kbox_dispatch_errno(-rc);
741-
742726
char linktrans[KBOX_MAX_PATH];
743-
rc = kbox_translate_path_for_lkl(pid, linkpathbuf, ctx->host_root,
744-
linktrans, sizeof(linktrans));
727+
long newdirfd;
728+
rc = translate_request_at_path(req, ctx, 1, 2, linktrans, sizeof(linktrans),
729+
&newdirfd);
745730
if (rc < 0)
746731
return kbox_dispatch_errno(-rc);
747-
748-
long newdirfd = resolve_open_dirfd(linktrans, newdirfd_raw, ctx->fd_table);
749-
if (newdirfd < 0 && newdirfd != AT_FDCWD_LINUX)
732+
if (should_continue_for_dirfd(newdirfd))
750733
return kbox_dispatch_continue();
751734

752735
/* Target is stored as-is (not translated). */
@@ -759,44 +742,25 @@ struct kbox_dispatch forward_symlinkat(const struct kbox_syscall_request *req,
759742
struct kbox_dispatch forward_linkat(const struct kbox_syscall_request *req,
760743
struct kbox_supervisor_ctx *ctx)
761744
{
762-
pid_t pid = kbox_syscall_request_pid(req);
763-
long olddirfd_raw = to_dirfd_arg(kbox_syscall_request_arg(req, 0));
764-
char oldpathbuf[KBOX_MAX_PATH];
765745
int rc;
766-
767-
rc = guest_mem_read_string(ctx, pid, kbox_syscall_request_arg(req, 1),
768-
oldpathbuf, sizeof(oldpathbuf));
769-
if (rc < 0)
770-
return kbox_dispatch_errno(-rc);
771-
772-
long newdirfd_raw = to_dirfd_arg(kbox_syscall_request_arg(req, 2));
773-
char newpathbuf[KBOX_MAX_PATH];
774-
775-
rc = guest_mem_read_string(ctx, pid, kbox_syscall_request_arg(req, 3),
776-
newpathbuf, sizeof(newpathbuf));
777-
if (rc < 0)
778-
return kbox_dispatch_errno(-rc);
779-
780746
long flags = to_c_long_arg(kbox_syscall_request_arg(req, 4));
781747

782748
char oldtrans[KBOX_MAX_PATH];
783-
rc = kbox_translate_path_for_lkl(pid, oldpathbuf, ctx->host_root, oldtrans,
784-
sizeof(oldtrans));
749+
long olddirfd;
750+
rc = translate_request_at_path(req, ctx, 0, 1, oldtrans, sizeof(oldtrans),
751+
&olddirfd);
785752
if (rc < 0)
786753
return kbox_dispatch_errno(-rc);
754+
if (should_continue_for_dirfd(olddirfd))
755+
return kbox_dispatch_continue();
787756

788757
char newtrans[KBOX_MAX_PATH];
789-
rc = kbox_translate_path_for_lkl(pid, newpathbuf, ctx->host_root, newtrans,
790-
sizeof(newtrans));
758+
long newdirfd;
759+
rc = translate_request_at_path(req, ctx, 2, 3, newtrans, sizeof(newtrans),
760+
&newdirfd);
791761
if (rc < 0)
792762
return kbox_dispatch_errno(-rc);
793-
794-
long olddirfd = resolve_open_dirfd(oldtrans, olddirfd_raw, ctx->fd_table);
795-
if (olddirfd < 0 && olddirfd != AT_FDCWD_LINUX)
796-
return kbox_dispatch_continue();
797-
798-
long newdirfd = resolve_open_dirfd(newtrans, newdirfd_raw, ctx->fd_table);
799-
if (newdirfd < 0 && newdirfd != AT_FDCWD_LINUX)
763+
if (should_continue_for_dirfd(newdirfd))
800764
return kbox_dispatch_continue();
801765

802766
long ret = kbox_lkl_linkat(ctx->sysnrs, olddirfd, oldtrans, newdirfd,
@@ -824,20 +788,12 @@ struct kbox_dispatch forward_utimensat(const struct kbox_syscall_request *req,
824788
int rc;
825789

826790
if (kbox_syscall_request_arg(req, 1) != 0) {
827-
char pathbuf[KBOX_MAX_PATH];
828-
rc = guest_mem_read_string(ctx, pid, kbox_syscall_request_arg(req, 1),
829-
pathbuf, sizeof(pathbuf));
791+
rc = translate_request_at_path(req, ctx, 0, 1, translated,
792+
sizeof(translated), &lkl_dirfd);
830793
if (rc < 0)
831794
return kbox_dispatch_errno(-rc);
832-
833-
rc = kbox_translate_path_for_lkl(pid, pathbuf, ctx->host_root,
834-
translated, sizeof(translated));
835-
if (rc < 0)
836-
return kbox_dispatch_errno(-rc);
837-
838795
translated_path = translated;
839-
lkl_dirfd = resolve_open_dirfd(translated, dirfd_raw, ctx->fd_table);
840-
if (lkl_dirfd < 0 && lkl_dirfd != AT_FDCWD_LINUX)
796+
if (should_continue_for_dirfd(lkl_dirfd))
841797
return kbox_dispatch_continue();
842798
} else {
843799
translated_path = NULL;

src/path.c

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,30 @@ static bool is_prefix_dir(const char *path, const char *prefix)
3232
return path[plen] == '\0' || path[plen] == '/';
3333
}
3434

35+
static const char *component_end(const char *s)
36+
{
37+
while (*s && *s != '/')
38+
s++;
39+
return s;
40+
}
41+
42+
static bool proc_escape_tail(const char *tail)
43+
{
44+
if (is_prefix_dir(tail, "root"))
45+
return true;
46+
if (is_prefix_dir(tail, "cwd"))
47+
return true;
48+
if (is_prefix_dir(tail, "exe"))
49+
return true;
50+
if (is_prefix_dir(tail, "fd"))
51+
return true;
52+
if (is_prefix_dir(tail, "fdinfo"))
53+
return true;
54+
if (is_prefix_dir(tail, "map_files"))
55+
return true;
56+
return false;
57+
}
58+
3559
/* Check if the string at @s is composed entirely of decimal digits.
3660
* Returns true for non-empty all-digit strings (i.e., a numeric PID).
3761
*/
@@ -81,8 +105,8 @@ bool kbox_is_proc_escape_path(const char *path)
81105
return false;
82106

83107
/* Must be "self", "thread-self", or a numeric PID. */
84-
bool is_self = (comp_len == 4 && memcmp(p, "self", 4) == 0);
85-
bool is_thread_self = (comp_len == 11 && memcmp(p, "thread-self", 11) == 0);
108+
bool is_self = (comp_len == 4 && !memcmp(p, "self", 4));
109+
bool is_thread_self = (comp_len == 11 && !memcmp(p, "thread-self", 11));
86110
if (!is_self && !is_thread_self && !is_numeric(p, comp_len))
87111
return false;
88112

@@ -107,15 +131,13 @@ bool kbox_is_proc_escape_path(const char *path)
107131
tail = tid_end + 1;
108132
}
109133

110-
/* Check for the dangerous symlink names. */
111-
if (is_prefix_dir(tail, "root"))
112-
return true;
113-
if (is_prefix_dir(tail, "cwd"))
114-
return true;
115-
if (is_prefix_dir(tail, "exe"))
116-
return true;
117-
118-
return false;
134+
/* Check for dangerous symlink/directory names.
135+
*
136+
* root, cwd, exe: magic symlinks that resolve to host filesystem locations.
137+
* fd, fdinfo: per-FD symlinks that resolve to host file objects.
138+
* map_files: memory-mapping symlinks that resolve to host file paths.
139+
*/
140+
return proc_escape_tail(tail);
119141
}
120142

121143
bool kbox_is_lkl_virtual_path(const char *path)
@@ -159,6 +181,55 @@ bool kbox_is_loader_runtime_path(const char *path)
159181
return false;
160182
}
161183

184+
bool kbox_relative_path_has_dotdot(const char *path)
185+
{
186+
if (!path)
187+
return false;
188+
189+
while (*path) {
190+
if (path[0] == '.' && path[1] == '.' &&
191+
(path[2] == '/' || path[2] == '\0'))
192+
return true;
193+
while (*path && *path != '/')
194+
path++;
195+
while (*path == '/')
196+
path++;
197+
}
198+
return false;
199+
}
200+
201+
bool kbox_relative_proc_escape_path(const char *path)
202+
{
203+
char probe[KBOX_MAX_PATH];
204+
const char *tail;
205+
const char *tid_end;
206+
size_t task_len;
207+
int n;
208+
209+
if (!path || path[0] == '\0')
210+
return false;
211+
212+
/* From a /proc dirfd, "self/root/..." and similar are already enough. */
213+
n = snprintf(probe, sizeof(probe), "/proc/%s", path);
214+
if (n > 0 && (size_t) n < sizeof(probe) && kbox_is_proc_escape_path(probe))
215+
return true;
216+
217+
/* From /proc/self, /proc/thread-self, or /proc/<pid>, look for direct
218+
* magic entries like "root" plus task/<tid>/root escapes.
219+
*/
220+
if (proc_escape_tail(path))
221+
return true;
222+
223+
if (!starts_with(path, "task/"))
224+
return false;
225+
tail = path + 5;
226+
tid_end = component_end(tail);
227+
task_len = (size_t) (tid_end - tail);
228+
if (task_len == 0 || !is_numeric(tail, task_len) || *tid_end != '/')
229+
return false;
230+
return proc_escape_tail(tid_end + 1);
231+
}
232+
162233
/* Lexical path normalization.
163234
*
164235
* Processes each component of input:

0 commit comments

Comments
 (0)