Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions liblfi/arch/x64/arch_sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ enum {
TUX_SYS_readv = 19,
TUX_SYS_writev = 20,
TUX_SYS_access = 21,
TUX_SYS_dup = 32,
TUX_SYS_dup2 = 33,
TUX_SYS_dup3 = 292,
TUX_SYS_sched_yield = 24,
TUX_SYS_mremap = 25,
TUX_SYS_madvise = 28,
Expand Down Expand Up @@ -82,6 +85,7 @@ enum {
TUX_SYS_unlinkat = 263,
TUX_SYS_renameat = 264,
TUX_SYS_readlinkat = 267,
TUX_SYS_fchmodat = 268,
TUX_SYS_faccessat = 269,
TUX_SYS_set_robust_list = 273,
TUX_SYS_utimensat = 280,
Expand Down
3 changes: 3 additions & 0 deletions liblfi/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ syshandle(struct TuxThread* p, uintptr_t sysno, uintptr_t a0, uintptr_t a1,
SYS(brk, sys_brk(proc, a0))
SYS(openat, sys_openat(proc, a0, a1, a2, a3))
SYS(close, sys_close(proc, a0))
SYS(dup, sys_dup(proc, a0))
SYS(dup3, sys_dup3(proc, a0, a1, a2))
SYS(writev, sys_writev(proc, a0, a1, a2))
SYS(readv, sys_readv(proc, a0, a1, a2))
SYS(pread64, sys_pread64(proc, a0, a1, a2, a3))
Expand All @@ -37,6 +39,7 @@ syshandle(struct TuxThread* p, uintptr_t sysno, uintptr_t a0, uintptr_t a1,
SYS(newfstatat, sys_newfstatat(proc, a0, a1, a2, a3))
SYS(fstat, sys_newfstatat(proc, a0, 0, a1, TUX_AT_EMPTY_PATH))
SYS(fchmod, sys_fchmod(proc, a0, a1))
SYS(fchmodat, sys_fchmodat(proc, a0, a1, a2, a3))
SYS(truncate, sys_truncate(proc, a0, a1))
SYS(ftruncate, sys_ftruncate(proc, a0, a1))
SYS(fchown, sys_fchown(proc, a0, a1, a2))
Expand Down
3 changes: 3 additions & 0 deletions liblfi/syscalls/strace.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ sysname(uint64_t sysno)
STRACE_CASE(brk)
STRACE_CASE(openat)
STRACE_CASE(close)
STRACE_CASE(dup)
STRACE_CASE(dup3)
STRACE_CASE(writev)
STRACE_CASE(readv)
STRACE_CASE(pread64)
Expand All @@ -25,6 +27,7 @@ sysname(uint64_t sysno)
STRACE_CASE(getrandom)
STRACE_CASE(clock_gettime)
STRACE_CASE(ioctl)
STRACE_CASE(fcntl)
STRACE_CASE(set_tid_address)
STRACE_CASE(set_robust_list)
STRACE_CASE(uname)
Expand Down
54 changes: 52 additions & 2 deletions liblfi/syscalls/sys_fcntl.c
Original file line number Diff line number Diff line change
@@ -1,11 +1,61 @@
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include "syscalls/syscalls.h"
#include "fd.h"
#include "file.h"
#include "host.h"
#include "host/posix/file.h"

int
sys_fcntl(struct TuxProc* p, int fd, int cmd, uintptr_t va0, uintptr_t va1,
uintptr_t va2, uintptr_t va3)
{
WARN(p->tux, "fcntl ignored");
return 0;
switch (cmd) {
case F_DUPFD: {
int minfd = va0;
struct FDFile* FD_DEFER(f) = fdget(&p->fdtable, fd);
if (!f)
return -TUX_EBADF;

// Find the lowest available fd >= minfd
for (int newfd = minfd; newfd < TUX_NOFILE; newfd++) {
if (!fdhas(&p->fdtable, newfd)) {
fdassign(&p->fdtable, newfd, f);
return newfd;
}
}
return -TUX_EMFILE;
}

case F_DUPFD_CLOEXEC:
// Close-on-exec not supported yet
return -TUX_ENOSYS;

case F_GETFD:
case F_SETFD:
case F_GETFL:
case F_SETFL: {
struct FDFile* FD_DEFER(f) = fdget(&p->fdtable, fd);
if (!f)
return -TUX_EBADF;

// Get the underlying host file descriptor
struct HostFile* hf = f->file ? f->file(f->dev) : NULL;
if (!hf)
return -TUX_EBADF;

// Pass through to system fcntl
int result = fcntl(hf->fd, cmd, va0);
if (result < 0)
return -errno;
return result;
}

default:
WARN(p->tux, "fcntl command %d not implemented", cmd);
return -TUX_ENOSYS;
}
}
48 changes: 48 additions & 0 deletions liblfi/syscalls/sys_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,42 @@ sys_close(struct TuxProc* p, int fd)
return 0;
}

int
sys_dup(struct TuxProc* p, int oldfd)
{
struct FDFile* FD_DEFER(f) = fdget(&p->fdtable, oldfd);
if (!f)
return -TUX_EBADF;

int newfd = fdalloc(&p->fdtable);
if (newfd < 0)
return -TUX_EMFILE;

fdassign(&p->fdtable, newfd, f);
return newfd;
}

int
sys_dup3(struct TuxProc* p, int oldfd, int newfd, int flags)
{
if (oldfd == newfd)
return -TUX_EINVAL;

struct FDFile* FD_DEFER(f) = fdget(&p->fdtable, oldfd);
if (!f)
return -TUX_EBADF;

if (newfd < 0 || newfd >= TUX_NOFILE)
return -TUX_EBADF;

if (flags != 0 && flags != TUX_O_CLOEXEC)
return -TUX_EINVAL;

fdremove(&p->fdtable, newfd);
fdassign(&p->fdtable, newfd, f);
return newfd;
}

ssize_t
sys_readlinkat(struct TuxProc* p, int dirfd, lfiptr_t pathp, lfiptr_t bufp, size_t size)
{
Expand Down Expand Up @@ -322,6 +358,18 @@ sys_fchmod(struct TuxProc* p, int fd, tux_mode_t mode)
return f->chmod(f->dev, mode);
}

int
sys_fchmodat(struct TuxProc* p, int dirfd, lfiptr_t pathp, tux_mode_t mode, int flags)
{
struct HostFile* dir = getfdir(p, dirfd);
if (dirfd != TUX_AT_FDCWD && !dir)
return -TUX_EBADF;
const char* path = procpath(p, pathp);
if (!path)
return -TUX_EFAULT;
return host_fchmodat(dir, path, mode, flags);
}

int
sys_fsync(struct TuxProc* p, int fd)
{
Expand Down
6 changes: 6 additions & 0 deletions liblfi/syscalls/syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ ssize_t sys_readlink(struct TuxProc* p, lfiptr_t pathp, lfiptr_t bufp, size_t si

int sys_close(struct TuxProc* p, int fd);

int sys_dup(struct TuxProc* p, int oldfd);

int sys_dup3(struct TuxProc* p, int oldfd, int newfd, int flags);

ssize_t sys_read(struct TuxProc* p, int fd, lfiptr_t bufp, size_t size);

ssize_t sys_readv(struct TuxProc* p, int fd, lfiptr_t iovp, size_t iovcnt);
Expand All @@ -122,6 +126,8 @@ int sys_fchmod(struct TuxProc* p, int fd, tux_mode_t mode);

int sys_chmod(struct TuxProc* p, uintptr_t pathp, tux_mode_t mode);

int sys_fchmodat(struct TuxProc* p, int dirfd, lfiptr_t pathp, tux_mode_t mode, int flags);

int sys_fchown(struct TuxProc* p, int fd, tux_uid_t owner, tux_gid_t group);

int sys_chown(struct TuxProc* p, uintptr_t pathp, tux_uid_t owner, tux_gid_t group);
Expand Down
9 changes: 8 additions & 1 deletion liblfi/test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ test('test_init', executable(
dependencies: [liblfi_dep]
), suite: ['liblfi'])

lficc = find_program(cpu + '-lfi-linux-musl-clang', required: false)
lficc = find_program([
cpu + '-lfi-linux-musl-clang',
'/opt/aarch64-lfi-clang-new/lfi-clang/' + cpu + '-lfi-linux-musl-clang',
'/opt/aarch64-lfi-clang/lfi-clang/' + cpu + '-lfi-linux-musl-clang',
'/opt/lfi/bin/' + cpu + '-lfi-linux-musl-clang'
], required: false)
if not lficc.found()
warning('lficc not found: cannot test liblfi')
subdir_done()
Expand Down Expand Up @@ -49,6 +54,8 @@ testdata = [
{'bin': test_tux, 'file': 'tux/file.c', 'inputs': ['tux/inputs/file.txt'], 'cflags': []},
{'bin': test_tux, 'file': 'tux/chdir.c', 'inputs': ['tux/inputs', 'file.txt'], 'cflags': []},
{'bin': test_tux, 'file': 'tux/thread.c', 'inputs': [], 'cflags': []},
{'bin': test_tux, 'file': 'tux/dup.c', 'inputs': [], 'cflags': []},
{'bin': test_tux, 'file': 'tux/fcntl.c', 'inputs': [], 'cflags': []},
{'bin': test_fs, 'file': 'tux/fs.c', 'inputs': '/proc/self/test.txt', 'cflags': []}
]

Expand Down
40 changes: 40 additions & 0 deletions liblfi/test/tux/dup.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

int main() {
// Test dup() syscall
int fd1 = open("/dev/null", O_WRONLY);
if (fd1 < 0) {
printf("failed to open /dev/null\n");
return 1;
}

int fd2 = dup(fd1);
if (fd2 < 0) {
printf("dup() failed\n");
return 1;
}

printf("dup: original fd=%d, dup fd=%d\n", fd1, fd2);

// Test dup3() syscall
int fd3 = 10; // arbitrary target fd
int fd4 = dup3(fd1, fd3, 0);
if (fd4 != fd3) {
printf("dup3() failed: expected %d, got %d\n", fd3, fd4);
return 1;
}

printf("dup3: original fd=%d, target fd=%d\n", fd1, fd3);

// Test writing to all fds to verify they work
const char* msg = "test\n";
write(fd1, msg, strlen(msg));
write(fd2, msg, strlen(msg));
write(fd3, msg, strlen(msg));

printf("dup test passed\n");
return 0;
}
3 changes: 3 additions & 0 deletions liblfi/test/tux/dup.c.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dup: original fd=3, dup fd=4
dup3: original fd=3, target fd=10
dup test passed
105 changes: 105 additions & 0 deletions liblfi/test/tux/fchmodat_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

int main() {
const char* testfile = "fchmodat_test_file.txt";
const char* testdir = "fchmodat_test_dir";

// Create a test directory
if (mkdir(testdir, 0755) != 0 && errno != EEXIST) {
fprintf(stderr, "Failed to create test directory: %s\n", strerror(errno));
return 1;
}

// Create a test file in the directory
char filepath[256];
snprintf(filepath, sizeof(filepath), "%s/%s", testdir, testfile);
FILE* f = fopen(filepath, "w");
if (!f) {
fprintf(stderr, "Failed to create test file: %s\n", strerror(errno));
rmdir(testdir);
return 1;
}
fprintf(f, "test content\n");
fclose(f);

// Open the directory for fchmodat
int dirfd = open(testdir, O_RDONLY);
if (dirfd < 0) {
fprintf(stderr, "Failed to open test directory: %s\n", strerror(errno));
unlink(filepath);
rmdir(testdir);
return 1;
}

// Test fchmodat - make file read-only using directory fd
printf("Testing fchmodat to make file read-only...\n");
if (fchmodat(dirfd, testfile, 0444, 0) != 0) {
fprintf(stderr, "fchmodat failed: %s\n", strerror(errno));
close(dirfd);
unlink(filepath);
rmdir(testdir);
return 1;
}

// Verify the permissions changed
struct stat st;
if (stat(filepath, &st) != 0) {
fprintf(stderr, "stat failed: %s\n", strerror(errno));
close(dirfd);
unlink(filepath);
rmdir(testdir);
return 1;
}

if ((st.st_mode & 0777) == 0444) {
printf("fchmodat test PASSED - file is now read-only\n");
} else {
printf("fchmodat test FAILED - expected mode 0444, got %o\n", st.st_mode & 0777);
close(dirfd);
unlink(filepath);
rmdir(testdir);
return 1;
}

// Test fchmodat with AT_FDCWD
printf("Testing fchmodat with AT_FDCWD...\n");
if (fchmodat(AT_FDCWD, filepath, 0644, 0) != 0) {
fprintf(stderr, "fchmodat with AT_FDCWD failed: %s\n", strerror(errno));
close(dirfd);
unlink(filepath);
rmdir(testdir);
return 1;
}

// Verify the permissions changed back
if (stat(filepath, &st) != 0) {
fprintf(stderr, "stat failed: %s\n", strerror(errno));
close(dirfd);
unlink(filepath);
rmdir(testdir);
return 1;
}

if ((st.st_mode & 0777) == 0644) {
printf("fchmodat with AT_FDCWD test PASSED - file is now writable\n");
} else {
printf("fchmodat with AT_FDCWD test FAILED - expected mode 0644, got %o\n", st.st_mode & 0777);
close(dirfd);
unlink(filepath);
rmdir(testdir);
return 1;
}

// Clean up
close(dirfd);
unlink(filepath);
rmdir(testdir);
printf("All fchmodat tests PASSED\n");
return 0;
}
5 changes: 5 additions & 0 deletions liblfi/test/tux/fchmodat_test.c.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Testing fchmodat to make file read-only...
fchmodat test PASSED - file is now read-only
Testing fchmodat with AT_FDCWD...
fchmodat with AT_FDCWD test PASSED - file is now writable
All fchmodat tests PASSED
Loading
Loading