Skip to content
Draft
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
12 changes: 11 additions & 1 deletion .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,14 @@ jobs:
run: curl -L -o /tmp/libkrunfw-5.0.0-x86_64.tgz https://github.com/containers/libkrunfw/releases/download/v5.0.0/libkrunfw-5.0.0-x86_64.tgz && mkdir tmp && tar xf /tmp/libkrunfw-5.0.0-x86_64.tgz -C tmp && sudo mv tmp/lib64/* /lib/x86_64-linux-gnu

- name: Integration tests
run: RUST_LOG=trace KRUN_ENOMEM_WORKAROUND=1 KRUN_NO_UNSHARE=1 make test
run: KRUN_ENOMEM_WORKAROUND=1 KRUN_NO_UNSHARE=1 KRUN_TEST_BASE_DIR=/tmp/libkrun-tests make test TEST_FLAGS="--keep-all --github-summary"

- name: Upload test logs
if: always()
uses: actions/upload-artifact@v4
with:
name: test-logs
path: |
/tmp/libkrun-tests/
!/tmp/libkrun-tests/**/guest-agent
if-no-files-found: ignore
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,8 @@ test-prefix/lib64/libkrun.pc: $(LIBRARY_RELEASE_$(OS))

test-prefix: test-prefix/lib64/libkrun.pc

TEST ?= all
TEST_FLAGS ?=

test: test-prefix
cd tests; LD_LIBRARY_PATH="$$(realpath ../test-prefix/lib64/)" PKG_CONFIG_PATH="$$(realpath ../test-prefix/lib64/pkgconfig/)" ./run.sh
cd tests; RUST_LOG=trace LD_LIBRARY_PATH="$$(realpath ../test-prefix/lib64/)" PKG_CONFIG_PATH="$$(realpath ../test-prefix/lib64/pkgconfig/)" ./run.sh test --test-case "$(TEST)" $(TEST_FLAGS)
26 changes: 23 additions & 3 deletions src/devices/src/virtio/vsock/tsi_dgram.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use std::num::Wrapping;
use std::os::fd::OwnedFd;
use std::os::unix::io::{AsRawFd, RawFd};
Expand All @@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex};
use nix::fcntl::{fcntl, FcntlArg, OFlag};
use nix::sys::socket::{
bind, connect, getpeername, recv, send, sendto, socket, AddressFamily, MsgFlags, SockFlag,
SockType, SockaddrIn, SockaddrLike, SockaddrStorage,
SockType, SockaddrIn, SockaddrLike, SockaddrStorage, UnixAddr,
};

#[cfg(target_os = "macos")]
Expand All @@ -35,6 +35,7 @@ pub struct TsiDgramProxy {
pub status: ProxyStatus,
sendto_addr: Option<SockaddrStorage>,
listening: bool,
family: AddressFamily,
mem: GuestMemoryMmap,
queue: Arc<Mutex<VirtQueue>>,
rxq: Arc<Mutex<MuxerRxQ>>,
Expand Down Expand Up @@ -102,6 +103,7 @@ impl TsiDgramProxy {
status: ProxyStatus::Idle,
sendto_addr: None,
listening: false,
family,
mem,
queue,
rxq,
Expand Down Expand Up @@ -339,7 +341,25 @@ impl Proxy for TsiDgramProxy {

self.sendto_addr = Some(req.addr);
if !self.listening {
match bind(self.fd.as_raw_fd(), &SockaddrIn::new(0, 0, 0, 0, 0)) {
let bind_result = match self.family {
AddressFamily::Inet => bind(self.fd.as_raw_fd(), &SockaddrIn::new(0, 0, 0, 0, 0)),
AddressFamily::Inet6 => {
let addr6: SockaddrStorage =
SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0).into();
bind(self.fd.as_raw_fd(), &addr6)
}
#[cfg(target_os = "linux")]
AddressFamily::Unix => {
let addr = UnixAddr::new_unnamed();
bind(self.fd.as_raw_fd(), &addr)
}
_ => {
warn!("sendto_addr: unsupported address family: {:?}", self.family);
return update;
}
};

match bind_result {
Ok(_) => {
self.listening = true;
update.polling = Some((self.id, self.fd.as_raw_fd(), EventSet::IN));
Expand Down
171 changes: 171 additions & 0 deletions tests/TSI_TESTING_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# TSI Testing Guide for AI Agents

This guide explains how to run and debug TSI (Transparent Socket Impersonation) tests for libkrun.

## Directory Structure

- **libkrun**: `~/Dev2/libkrun/t/test-fix-tsi` - contains test code and libkrun source
- **libkrunfw**: `~/Dev2/libkrunfw/t/tsi-dgram-fix` - contains kernel patches
- **test-prefix**: `~/Dev2/libkrun/t/test-fix-tsi/test-prefix` - where libraries are installed for testing

## Running Tests

**IMPORTANT: Always use `make test` from the libkrun directory. Do NOT use `./run.sh` directly - the Makefile handles library paths and environment setup correctly.**

### Run all tests
```bash
cd ~/Dev2/libkrun/t/test-fix-tsi
make test
```

### Run a specific test
```bash
cd ~/Dev2/libkrun/t/test-fix-tsi
make test TEST=tsi-unix-dgram-setsockopt
```

### Run with timeout (useful for tests that may hang on kernel panic)
```bash
cd ~/Dev2/libkrun/t/test-fix-tsi
timeout 30 make test TEST=tsi-unix-dgram-setsockopt
```

### Specify custom output directory (recommended for debugging)

Use `--base-dir` to specify where test artifacts are saved. Use a timestamped path to avoid conflicts:

```bash
cd ~/Dev2/libkrun/t/test-fix-tsi
make test TEST=tsi-unix-dgram-setsockopt TEST_FLAGS="--base-dir /tmp/tsi-test-$(date +%Y%m%d-%H%M%S) --keep-all"
```

This creates a predictable output location like `/tmp/tsi-test-20251201-143628/` containing:
- `<test-name>/log.txt` - full test log with kernel output
- `<test-name>/root/` - guest filesystem artifacts

### Keep test artifacts for debugging
```bash
make test TEST=tsi-unix-dgram-setsockopt TEST_FLAGS="--keep-all"
```

Note: Without `--keep-all`, artifacts are only kept for failed tests.

## Switching Kernels

Tests use whatever libkrunfw is installed in `test-prefix/lib64/`.

### Install patched kernel (tsi-dgram-fix branch)
```bash
cd ~/Dev2/libkrunfw/t/tsi-dgram-fix
PREFIX="$(realpath ~/Dev2/libkrun/t/test-fix-tsi/test-prefix)" make install
```

### Install unpatched kernel (main branch) to verify bugs exist
```bash
cd ~/Dev2/libkrunfw/t/main
PREFIX="$(realpath ~/Dev2/libkrun/t/test-fix-tsi/test-prefix)" make install
```

### Safely remove libkrunfw before switching
```bash
rm -f ~/Dev2/libkrun/t/test-fix-tsi/test-prefix/lib64/libkrunfw*
```

## Debugging Kernel Panics

### Viewing kernel output

Filter for kernel messages in the log file:
```bash
grep "init_or_kernel]" /tmp/tsi-test-XXXXXXXX/tsi-unix-dgram-setsockopt/log.txt
```

This shows kernel boot messages and panic traces.

### Enabling verbose kernel output

By default, kernel output is suppressed. To see kernel panics and debug messages, edit:
```
src/vmm/src/vmm_config/kernel_cmdline.rs
```

**Default (quiet, no panic output):**
```rust
pub const DEFAULT_KERNEL_CMDLINE: &str = "reboot=k panic=-1 panic_print=0 nomodule console=hvc0 \
rootfstype=virtiofs rw quiet no-kvmapf";
```

**For debugging (verbose, shows panics):**
```rust
pub const DEFAULT_KERNEL_CMDLINE: &str = "reboot=k panic=-1 nomodule console=hvc0 \
rootfstype=virtiofs rw no-kvmapf";
```

Changes:
- Remove `quiet` - shows all kernel boot messages
- Remove `panic_print=0` - shows full panic information

**Remember to restore the original settings after debugging!**

### Example: Finding a kernel panic

1. Enable verbose kernel output (edit kernel_cmdline.rs as above)
2. Run the test with timeout and custom output dir:
```bash
cd ~/Dev2/libkrun/t/test-fix-tsi
timeout 30 make test TEST=tsi-unix-dgram-setsockopt TEST_FLAGS="--base-dir /tmp/tsi-test-$(date +%Y%m%d-%H%M%S) --keep-all"
```
3. Filter for kernel messages:
```bash
grep "init_or_kernel]" /tmp/tsi-test-XXXXXXXX/tsi-unix-dgram-setsockopt/log.txt
```
4. Look for `BUG:`, `Oops:`, `NULL pointer dereference`, `Call Trace:` etc.

### Example kernel panic output
```
[ 0.060929] BUG: kernel NULL pointer dereference, address: 0000000000000000
[ 0.061037] #PF: supervisor instruction fetch in kernel mode
[ 0.061126] #PF: error_code(0x0010) - not-present page
[ 0.061239] Oops: Oops: 0010 [#1] PREEMPT SMP NOPTI
[ 0.061311] CPU: 0 UID: 0 PID: 318 Comm: guest-agent Not tainted 6.12.44 #1
[ 0.061398] RIP: 0010:0x0
[ 0.062476] Call Trace:
[ 0.062527] <TASK>
[ 0.062561] ? tsi_dgram_setsockopt+0x6a/0x90
[ 0.062644] ? do_sock_setsockopt+0xaa/0x190
[ 0.062736] ? __sys_setsockopt+0x5d/0xb0
```

This shows a NULL pointer dereference in `tsi_dgram_setsockopt`.

## Test Files

- Test cases: `tests/test_cases/src/`
- Test runner: `tests/runner/src/main.rs`

### Adding a new test

1. Create `tests/test_cases/src/test_<name>.rs`
2. Register in `tests/test_cases/src/lib.rs`:
```rust
mod test_<name>;
use test_<name>::Test<Name>;

// In test_cases() function:
TestCase::new("test-name", Box::new(Test<Name>)),
```

## Common Issues

### Test hangs indefinitely
- Usually indicates a kernel panic or deadlock
- Use `timeout` to prevent waiting forever
- Check logs for kernel panic messages

### "cannot open shared object file: libkrun.so.1"
- Always use `make test` which sets up LD_LIBRARY_PATH correctly
- Do NOT run `./run.sh` directly

### Cargo build lock
- If you see "Blocking waiting for file lock on build directory"
- Another cargo process is running, wait for it or kill it
6 changes: 5 additions & 1 deletion tests/guest-agent/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ fn run_guest_agent(test_name: &str) -> anyhow::Result<()> {
.into_iter()
.find(|t| t.name() == test_name)
.context("No such test!")?;
let TestCase { test, name: _ } = test_case;
let TestCase {
test,
name: _,
requires_namespace: _,
} = test_case;
test.in_guest();
Ok(())
}
Expand Down
14 changes: 11 additions & 3 deletions tests/run.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#/bin/sh
#!/bin/sh

# This script has to be run with the working directory being "test"
# This runs the tests on the libkrun instance found by pkg-config.
Expand All @@ -16,11 +16,19 @@ cargo build -p runner

export KRUN_TEST_GUEST_AGENT_PATH="target/$GUEST_TARGET_ARCH/debug/guest-agent"

# Build runner args: pass through all arguments
RUNNER_ARGS="$*"

# Add --base-dir if KRUN_TEST_BASE_DIR is set
if [ -n "${KRUN_TEST_BASE_DIR}" ]; then
RUNNER_ARGS="${RUNNER_ARGS} --base-dir ${KRUN_TEST_BASE_DIR}"
fi

if [ -z "${KRUN_NO_UNSHARE}" ] && which unshare 2>&1 >/dev/null; then
unshare --user --map-root-user --net -- /bin/sh -c "ifconfig lo 127.0.0.1 && exec target/debug/runner $@"
unshare --user --map-root-user --net -- /bin/sh -c "ifconfig lo 127.0.0.1 && exec target/debug/runner ${RUNNER_ARGS}"
else
echo "WARNING: Running tests without a network namespace."
echo "Tests may fail if the required network ports are already in use."
echo
target/debug/runner $@
target/debug/runner ${RUNNER_ARGS}
fi
2 changes: 1 addition & 1 deletion tests/runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
test_cases = { path = "../test_cases", features = ["host"] }
anyhow = "1.0.95"
nix = { version = "0.29.0", features = ["resource", "fs"] }
nix = { version = "0.29.0", features = ["resource", "fs", "sched", "user", "process"] }
macros = { path = "../macros" }
clap = { version = "4.5.27", features = ["derive"] }
tempdir = "0.3.7"
Loading