Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zpoline integration for an option to hijack library #524

Merged
merged 5 commits into from
Aug 1, 2023
Merged
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
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ jobs:
runs_on: ubuntu-22.04
shell: bash
build_options: "LKL_FUZZING=1 fuzzers"
- displayTargetName: zpoline
# maybe integrate with default Linux build once the function becomes stable
os: unix
runs_on: ubuntu-22.04
shell: bash
build_options: "zpoline=./zpoline"
timeout-minutes: 100
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
Expand Down Expand Up @@ -128,6 +134,20 @@ jobs:
which gcc
ccache -z

- name: install zpoline
if: matrix.displayTargetName == 'zpoline'
thehajime marked this conversation as resolved.
Show resolved Hide resolved
run: |
sudo apt install -y binutils-dev
git clone https://github.com/yasukata/zpoline
cd zpoline
git checkout 022a3b8c7a5c23bfd99162b478bf3eb5f70c07a2
make
cd ..
# This is the whole point of zpoline
echo "==== setting mmap_min_addr ===="
sudo sh -c "echo 0 > /proc/sys/vm/mmap_min_addr"
echo "setting env variable (debug)"
echo "ZPOLINE_DEBUG=0" >> "$GITHUB_ENV"
- name: Build
run: |
make -j4 -C tools/lkl ${{ matrix.build_options }}
Expand Down
65 changes: 65 additions & 0 deletions Documentation/lkl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,71 @@ The following are the list of keys to describe a JSON file.
"nameserver":"8.8.8.8"
```

LKL hijack library with zpoline
-------------------------------

[zpoline](https://github.com/yasukata/zpoline) is an alternative to
syscall hijack based on LD_PRELOAD, which is still default on LKL.
The zpoline library works with binary rewrites to the loaded programs
upon instantiation, then load hook function for the original syscalls.
The LKL hijack library works together with zpoline by loading LKL.

thehajime marked this conversation as resolved.
Show resolved Hide resolved
zpoline currently only works on x86_64 machines.

To use the zpoline-enabled hijack library, please follow the
instruction below.

- Build
```
make -C tools/lkl -j8 zpoline=../zpoline
```

Suppose `zpoline` is downloaded at `../zpoline` and already build
before LKL build.

- Execution

zpoline rewrites the memory address 0x0 to hook syscalls, but non-root
users don't have a privilege to operate that address. The following
configuration allows us to use zpoline without root privilege.

```
sudo sh -c "echo 0 > /proc/sys/vm/mmap_min_addr"
```

then, execute command with the environment variable `LKL_HIJACK_ZPOLINE=1`.

```
LKL_HIJACK_ZPOLINE=1 LKL_HIJACK_CONFIG_FILE=lkl-tap.json \
./tools/lkl/bin/lkl-hijack.sh ping www.google.com
```

The file `lkl-tap.json` can be prepared like this.

```
{
"gateway": "172.17.0.1",
"nameserver": "8.8.8.8",
"interfaces": [
{
"ip": "172.17.0.39",
"masklen": "16",
"mac": "00:0d:0b:94:4e:97",
"param": "tap0",
"type": "tap"
}
],
}
```

With the preload hijack library, which is the default one, it uses the
host name resolver and if the host uses a nameserver, defined at
`/etc/resolv.conf`, like 127.0.0.53, is not accepting DNS requests, in
a view of the LKL instance.

But with zpoline, it can successfully replace all syscalls for name
resolution so can `ping` with a name.

FAQ
===

Expand Down
1 change: 1 addition & 0 deletions tools/lkl/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ tests/net-test
tests/disk
tests/disk-vfio-pci
tests/config
tests/test-dlmopen
Makefile.conf
include/lkl_autoconf.h
include/kernel_config.h
Expand Down
6 changes: 6 additions & 0 deletions tools/lkl/Makefile.autoconf
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ define virtio_net_vde
LDLIBS += $(shell pkg-config --libs vdeplug)
endef

define zpoline_conf
$(eval zpoline_dir=$(abspath $(srctree)/$(1)))
$(if $(strip $(foreach f, $(zpoline_dir), $(wildcard $(f)/libzpoline.so))),$(call set_autoconf_var,ZPOLINE_DIR,$(zpoline_dir)))
endef

define posix_host
$(call set_autoconf_var,POSIX,y)
$(call set_autoconf_var,VIRTIO_NET,y)
Expand All @@ -82,6 +87,7 @@ define posix_host
$(if $(filter $(1),elf64-x86-64-freebsd),$(call set_autoconf_var,NEEDS_LARGP,y))
$(if $(filter $(1),elf32-i386),$(call set_autoconf_var,I386,y))
$(if $(strip $(call find_include,jsmn.h)),$(call set_autoconf_var,JSMN,y))
$(if $(filter %,$(zpoline)),$(call zpoline_conf,$(zpoline)))
endef

define nt64_host
Expand Down
10 changes: 9 additions & 1 deletion tools/lkl/Targets
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ libs-y += lib/liblkl

ifneq ($(LKL_HOST_CONFIG_BSD),y)
libs-$(LKL_HOST_CONFIG_POSIX) += lib/hijack/liblkl-hijack
libs-$(LKL_HOST_CONFIG_POSIX) += lib/hijack/liblkl-zpoline
endif
LDFLAGS_lib/hijack/liblkl-hijack-y += -shared -nodefaultlibs
LDLIBS_lib/hijack/liblkl-hijack-y += -ldl
LDLIBS_lib/hijack/liblkl-hijack-$(LKL_HOST_CONFIG_ARM) += -lgcc -lc
LDLIBS_lib/hijack/liblkl-hijack-$(LKL_HOST_CONFIG_AARCH64) += -lc
LDLIBS_lib/hijack/liblkl-hijack-$(LKL_HOST_CONFIG_AARCH64) += -lgcc -lc
LDLIBS_lib/hijack/liblkl-hijack-$(LKL_HOST_CONFIG_I386) += -lc_nonshared

LDFLAGS_lib/hijack/liblkl-zpoline-$(LKL_HOST_CONFIG_POSIX) += -shared -nodefaultlibs
LDLIBS_lib/hijack/liblkl-zpoline-$(LKL_HOST_CONFIG_POSIX) += -ldl -lc

progs-$(LKL_HOST_CONFIG_FUSE) += lklfuse
LDLIBS_lklfuse-y := -lfuse

Expand All @@ -26,6 +30,10 @@ progs-y += tests/disk
progs-y += tests/disk-vfio-pci
progs-y += tests/net-test
progs-y += tests/config
ifneq ($(LKL_HOST_CONFIG_BSD),y)
progs-y += tests/test-dlmopen
LDLIBS_tests/test-dlmopen-$(LKL_HOST_CONFIG_POSIX) += -ldl
endif

# LKL fuzzers
fuzzers-y += fuzzers/hid/hid-fuzzer
Expand Down
11 changes: 10 additions & 1 deletion tools/lkl/bin/lkl-hijack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,19 @@
##

script_dir=$(cd $(dirname ${BASH_SOURCE:-$0}); pwd)
. ${script_dir}/../tests/autoconf.sh

export LD_LIBRARY_PATH=${script_dir}/../lib/hijack
if [ -n ${LKL_HIJACK_DEBUG+x} ]
then
trap '' TSTP
fi
LD_PRELOAD=liblkl-hijack.so $*


if [ -n "${LKL_HIJACK_ZPOLINE}" ]
then
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${LKL_HOST_CONFIG_ZPOLINE_DIR}
LD_PRELOAD=libzpoline.so LIBZPHOOK=liblkl-zpoline.so $*
else
LD_PRELOAD=liblkl-hijack.so $*
fi
1 change: 1 addition & 0 deletions tools/lkl/include/lkl_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern "C" {
#include <lkl.h>

extern struct lkl_host_operations lkl_host_ops;
extern void lkl_change_tls_mode(void);

/**
* lkl_printf - print a message via the host print operation
Expand Down
5 changes: 5 additions & 0 deletions tools/lkl/lib/hijack/Build
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
liblkl-hijack-y += preload.o
liblkl-hijack-y += hijack.o
liblkl-hijack-y += init.o
liblkl-hijack-y += xlate.o

liblkl-zpoline-y += zpoline.o
liblkl-zpoline-y += hijack.o
liblkl-zpoline-y += init.o
liblkl-zpoline-y += xlate.o
31 changes: 15 additions & 16 deletions tools/lkl/lib/hijack/hijack.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@

#include "xlate.h"
#include "init.h"
#include "hijack.h"

static int is_lklfd(int fd)
int is_lklfd(int fd)
{
if (fd < LKL_FD_OFFSET)
return 0;
Expand Down Expand Up @@ -167,8 +168,8 @@ HOST_CALL(write)
HOST_CALL(pipe2)

HOST_CALL(setsockopt);
int setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen)
int hijack_setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen)
{
CHECK_HOST_CALL(setsockopt);
if (!is_lklfd(fd))
Expand All @@ -178,7 +179,7 @@ int setsockopt(int fd, int level, int optname, const void *optval,
}

HOST_CALL(getsockopt);
int getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
int hijack_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen)
{
CHECK_HOST_CALL(getsockopt);
if (!is_lklfd(fd))
Expand Down Expand Up @@ -240,7 +241,7 @@ int fcntl(int fd, int cmd, ...)
}

HOST_CALL(poll);
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
int hijack_poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
unsigned int i, lklfds = 0, hostfds = 0;

Expand All @@ -264,10 +265,8 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout)
return lkl_sys_poll((struct lkl_pollfd *)fds, nfds, timeout);
}

int __poll(struct pollfd *, nfds_t, int) __attribute__((alias("poll")));

HOST_CALL(select);
int select(int nfds, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
int hijack_select(int nfds, fd_set *r, fd_set *w, fd_set *e, struct timeval *t)
{
int fd, hostfds = 0, lklfds = 0;

Expand Down Expand Up @@ -324,7 +323,7 @@ int close(int fd)
}

HOST_CALL(epoll_create);
int epoll_create(int size)
int hijack_epoll_create(int size)
{
int host_fd;

Expand All @@ -346,14 +345,14 @@ int epoll_create(int size)
}

HOST_CALL(epoll_create1);
int epoll_create1(int flags)
int hijack_epoll_create1(int flags)
{
int host_fd;

CHECK_HOST_CALL(epoll_create1);

host_fd = host_epoll_create1(flags);
if (!host_fd) {
if (host_fd < 0) {
fprintf(stderr, "%s fail (%d)\n", __func__, errno);
return -1;
}
Expand All @@ -369,7 +368,7 @@ int epoll_create1(int flags)


HOST_CALL(epoll_ctl);
int epoll_ctl(int epollfd, int op, int fd, struct epoll_event *event)
int hijack_epoll_ctl(int epollfd, int op, int fd, struct epoll_event *event)
{
CHECK_HOST_CALL(epoll_ctl);

Expand Down Expand Up @@ -404,7 +403,7 @@ static void *host_epollwait(void *arg)
return (void *)(intptr_t)ret;
}

int epoll_wait(int epfd, struct epoll_event *events,
int hijack_epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout)
{
CHECK_HOST_CALL(epoll_wait);
Expand Down Expand Up @@ -541,7 +540,7 @@ int epoll_wait(int epfd, struct epoll_event *events,
return ret;
}

int eventfd(unsigned int count, int flags)
int hijack_eventfd(unsigned int count, int flags)
{
if (!lkl_running) {
int (*f)(unsigned int, int) = resolve_sym("eventfd");
Expand All @@ -553,7 +552,7 @@ int eventfd(unsigned int count, int flags)
}

HOST_CALL(eventfd_read);
int eventfd_read(int fd, uint64_t *value)
int hijack_eventfd_read(int fd, uint64_t *value)
{
CHECK_HOST_CALL(eventfd_read);

Expand All @@ -565,7 +564,7 @@ int eventfd_read(int fd, uint64_t *value)
}

HOST_CALL(eventfd_write);
int eventfd_write(int fd, uint64_t value)
int hijack_eventfd_write(int fd, uint64_t value)
{
CHECK_HOST_CALL(eventfd_write);

Expand Down
21 changes: 21 additions & 0 deletions tools/lkl/lib/hijack/hijack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 */

#include <sys/socket.h>
#include <sys/epoll.h>
#include <poll.h>


int is_lklfd(int fd);
int hijack_setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen);
int hijack_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen);
int hijack_poll(struct pollfd *fds, nfds_t nfds, int timeout);
int hijack_select(int nfds, fd_set *r, fd_set *w, fd_set *e, struct timeval *t);
int hijack_eventfd(unsigned int count, int flags);
int hijack_epoll_create(int size);
int hijack_epoll_create1(int flags);
int hijack_epoll_ctl(int epollfd, int op, int fd, struct epoll_event *event);
int hijack_epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
int hijack_eventfd_read(int fd, uint64_t *value);
int hijack_eventfd_write(int fd, uint64_t value);
26 changes: 22 additions & 4 deletions tools/lkl/lib/hijack/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ static int config_load(void)
return ret;
}

void __attribute__((constructor))
hijack_init(void)
void
__hijack_init(void)
{
int ret, i, dev_null;
int single_cpu_mode = 0;
Expand Down Expand Up @@ -225,8 +225,17 @@ hijack_init(void)
lkl_load_config_post(cfg);
}

void __attribute__((destructor))
hijack_fini(void)
void __attribute__((constructor))
hijack_init(void)
{
if (getenv("LKL_HIJACK_ZPOLINE"))
return;

return __hijack_init();
}

void
__hijack_fini(void)
{
int i;
int err;
Expand Down Expand Up @@ -257,3 +266,12 @@ hijack_fini(void)

lkl_cleanup();
}

void __attribute__((destructor))
hijack_fini(void)
{
if (getenv("LKL_HIJACK_ZPOLINE"))
return;

return __hijack_fini();
}
3 changes: 3 additions & 0 deletions tools/lkl/lib/hijack/init.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@
extern int lkl_running;
extern int dual_fds[];

void __hijack_init(void);
void __hijack_fini(void);

#endif /*_LKL_HIJACK_INIT_H */
Loading
Loading