LATX, fix: track IPC namespace joined with setns
A guest can enter an IPC namespace through setns() with either an explicit CLONE_NEWIPC type or a zero type inferred from the namespace fd. Record both successful paths so the AOT exit worker remains synchronous and cannot keep the namespace alive after guest exit. Inspect NS_GET_NSTYPE only for the zero-type namespace-fd form and preserve the real setns() result when that ioctl is not applicable. This keeps pidfd namespace masks working while handling invalid descriptors explicitly. Add integration coverage for invalid descriptors, pidfd namespace joins, and both namespace-fd forms with AOT and mqueue lifecycle validation. Signed-off-by: yuerengan <y347812075@163.com>
This commit is contained in:
parent
7523f1ff5e
commit
8299cef200
|
|
@ -17852,7 +17852,23 @@ defined(__loongarch__)
|
|||
|
||||
#if defined(TARGET_NR_setns) && defined(CONFIG_SETNS)
|
||||
case TARGET_NR_setns:
|
||||
return get_errno(setns(arg1, arg2));
|
||||
{
|
||||
bool joins_ipc_namespace = (arg2 & CLONE_NEWIPC) != 0;
|
||||
|
||||
if (arg2 == 0) {
|
||||
int namespace_type = ioctl(arg1, NS_GET_NSTYPE);
|
||||
|
||||
if (namespace_type >= 0) {
|
||||
joins_ipc_namespace = namespace_type == CLONE_NEWIPC;
|
||||
}
|
||||
}
|
||||
|
||||
ret = get_errno(setns(arg1, arg2));
|
||||
if (ret == 0 && joins_ipc_namespace) {
|
||||
((TaskState *)cpu->opaque)->ipc_namespace_isolated = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#ifdef TARGET_NR_seccomp
|
||||
case TARGET_NR_seccomp:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 4:
|
||||
raise SystemExit("usage: exec-with-pidfd.py PID EMULATOR GUEST")
|
||||
|
||||
pidfd = os.pidfd_open(int(sys.argv[1]))
|
||||
os.dup2(pidfd, 9, inheritable=True)
|
||||
env = os.environ.copy()
|
||||
env["LATX_AOT"] = "0"
|
||||
env["LATX_KZT"] = "0"
|
||||
os.execve(sys.argv[2], [sys.argv[2], sys.argv[3]], env)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -55,4 +55,16 @@ if host_machine.cpu_family() == 'loongarch64' and \
|
|||
suite: 'latx-integration',
|
||||
timeout: 30,
|
||||
)
|
||||
test(
|
||||
'test-setns-ipc-mqueue',
|
||||
find_program('test-setns-ipc-mqueue.sh'),
|
||||
args: [
|
||||
emulators['latx-x86_64'],
|
||||
files('setns-ipc-mqueue.S'),
|
||||
files('exec-with-pidfd.py'),
|
||||
],
|
||||
protocol: 'exitcode',
|
||||
suite: 'latx-integration',
|
||||
timeout: 30,
|
||||
)
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
.equ __NR_close, 3
|
||||
.equ __NR_mount, 165
|
||||
.equ __NR_exit_group, 231
|
||||
.equ __NR_setns, 308
|
||||
|
||||
#ifndef SETNS_FD
|
||||
#define SETNS_FD 9
|
||||
#endif
|
||||
|
||||
#ifndef EXPECT_SETNS_ERRNO
|
||||
#define EXPECT_SETNS_ERRNO 0
|
||||
#endif
|
||||
|
||||
#ifndef SETNS_ONLY
|
||||
#define SETNS_ONLY 0
|
||||
#endif
|
||||
|
||||
.section .rodata
|
||||
mq_source:
|
||||
.asciz "mqueue"
|
||||
mq_target:
|
||||
.asciz "mqueue"
|
||||
mq_type:
|
||||
.asciz "mqueue"
|
||||
|
||||
.section .text
|
||||
.global _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
mov $__NR_setns, %eax
|
||||
mov $SETNS_FD, %edi
|
||||
mov $SETNS_FLAGS, %esi
|
||||
syscall
|
||||
#if EXPECT_SETNS_ERRNO
|
||||
cmp $-EXPECT_SETNS_ERRNO, %rax
|
||||
jne setns_error_mismatch
|
||||
xor %edi, %edi
|
||||
jmp exit
|
||||
#else
|
||||
test %rax, %rax
|
||||
js setns_failed
|
||||
|
||||
mov $__NR_close, %eax
|
||||
mov $SETNS_FD, %edi
|
||||
syscall
|
||||
|
||||
#if SETNS_ONLY
|
||||
xor %edi, %edi
|
||||
jmp exit
|
||||
#endif
|
||||
|
||||
mov $__NR_mount, %eax
|
||||
lea mq_source(%rip), %rdi
|
||||
lea mq_target(%rip), %rsi
|
||||
lea mq_type(%rip), %rdx
|
||||
xor %r10d, %r10d
|
||||
xor %r8d, %r8d
|
||||
syscall
|
||||
test %rax, %rax
|
||||
js mount_failed
|
||||
|
||||
xor %edi, %edi
|
||||
jmp exit
|
||||
#endif
|
||||
|
||||
setns_failed:
|
||||
mov $10, %edi
|
||||
jmp exit
|
||||
|
||||
setns_error_mismatch:
|
||||
mov $11, %edi
|
||||
jmp exit
|
||||
|
||||
mount_failed:
|
||||
mov $20, %edi
|
||||
|
||||
exit:
|
||||
mov $__NR_exit_group, %eax
|
||||
syscall
|
||||
.size _start, .-_start
|
||||
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
emulator=$(cd "$(dirname "$1")" && pwd)/$(basename "$1")
|
||||
source_file=$2
|
||||
pidfd_runner=$3
|
||||
workdir=$(mktemp -d)
|
||||
trap 'rm -rf "$workdir"' EXIT HUP INT TERM
|
||||
|
||||
if ! command -v unshare >/dev/null 2>&1 ||
|
||||
! unshare -Urm true 2>/dev/null; then
|
||||
echo "SKIP: unprivileged user and mount namespaces are unavailable"
|
||||
exit 77
|
||||
fi
|
||||
|
||||
compile_guest()
|
||||
{
|
||||
output=$1
|
||||
shift
|
||||
"$clang" --target=x86_64-linux-gnu -fuse-ld=lld -nostdlib -static \
|
||||
-Wl,--build-id=none "$@" "$source_file" -o "$output"
|
||||
}
|
||||
|
||||
run_invalid_fd_case()
|
||||
{
|
||||
guest="$workdir/setns-invalid-fd"
|
||||
|
||||
compile_guest "$guest" -DSETNS_FLAGS=0 -DSETNS_FD=-1 \
|
||||
-DEXPECT_SETNS_ERRNO=9
|
||||
LATX_AOT=0 LATX_KZT=0 "$emulator" "$guest"
|
||||
echo "PASS: invalid namespace fd returned EBADF"
|
||||
}
|
||||
|
||||
run_pidfd_case()
|
||||
{
|
||||
guest="$workdir/setns-ipc-pidfd"
|
||||
|
||||
if ! command -v python3 >/dev/null 2>&1 ||
|
||||
! python3 -c 'import os; fd = os.pidfd_open(os.getpid()); os.close(fd)';
|
||||
then
|
||||
echo "SKIP: pidfd support is unavailable"
|
||||
return
|
||||
fi
|
||||
|
||||
compile_guest "$guest" -DSETNS_FLAGS=134217728 -DSETNS_ONLY=1
|
||||
unshare -Ur sh -eu -c '
|
||||
emulator=$1
|
||||
guest=$2
|
||||
pidfd_runner=$3
|
||||
|
||||
unshare -i sh -c "exec sleep 300" &
|
||||
holder=$!
|
||||
current_ipc=$(readlink /proc/self/ns/ipc)
|
||||
namespace_ready=false
|
||||
attempts=0
|
||||
while [ "$attempts" -lt 100 ]; do
|
||||
if [ -r "/proc/$holder/ns/ipc" ] &&
|
||||
[ "$(readlink "/proc/$holder/ns/ipc")" != "$current_ipc" ]; then
|
||||
namespace_ready=true
|
||||
break
|
||||
fi
|
||||
attempts=$((attempts + 1))
|
||||
sleep 0.01
|
||||
done
|
||||
if [ "$namespace_ready" != true ]; then
|
||||
kill "$holder" 2>/dev/null || true
|
||||
wait "$holder" 2>/dev/null || true
|
||||
echo "FAIL: pidfd IPC namespace holder did not start" >&2
|
||||
exit 30
|
||||
fi
|
||||
|
||||
set +e
|
||||
"$pidfd_runner" "$holder" "$emulator" "$guest"
|
||||
ret=$?
|
||||
set -e
|
||||
kill "$holder" 2>/dev/null || true
|
||||
wait "$holder" 2>/dev/null || true
|
||||
if [ "$ret" -ne 0 ]; then
|
||||
echo "FAIL: pidfd setns guest exited with status $ret" >&2
|
||||
exit "$ret"
|
||||
fi
|
||||
echo "PASS: pidfd joined the IPC namespace"
|
||||
' sh "$emulator" "$guest" "$pidfd_runner"
|
||||
}
|
||||
|
||||
if command -v clang-19 >/dev/null 2>&1; then
|
||||
clang=clang-19
|
||||
elif command -v clang >/dev/null 2>&1; then
|
||||
clang=clang
|
||||
else
|
||||
echo "SKIP: clang is required to build the x86_64 guest"
|
||||
exit 77
|
||||
fi
|
||||
|
||||
run_case()
|
||||
{
|
||||
setns_flags=$1
|
||||
case_name=$2
|
||||
guest="$workdir/setns-ipc-mqueue-$case_name"
|
||||
mkdir "$workdir/$case_name"
|
||||
|
||||
compile_guest "$guest" -DSETNS_FLAGS="$setns_flags"
|
||||
|
||||
unshare -Urm sh -eu -c '
|
||||
emulator=$1
|
||||
guest=$2
|
||||
case_name=$3
|
||||
workdir=$4
|
||||
cd "$workdir"
|
||||
mkdir mqueue
|
||||
mount --make-rprivate /
|
||||
trap "umount mqueue 2>/dev/null || true" EXIT HUP INT TERM
|
||||
|
||||
unshare -i sh -c "exec sleep 300" &
|
||||
holder=$!
|
||||
current_ipc=$(readlink /proc/self/ns/ipc)
|
||||
namespace_ready=false
|
||||
attempts=0
|
||||
while [ "$attempts" -lt 100 ]; do
|
||||
if [ -r "/proc/$holder/ns/ipc" ] &&
|
||||
[ "$(readlink "/proc/$holder/ns/ipc")" != "$current_ipc" ]; then
|
||||
namespace_ready=true
|
||||
break
|
||||
fi
|
||||
attempts=$((attempts + 1))
|
||||
sleep 0.01
|
||||
done
|
||||
if [ "$namespace_ready" != true ]; then
|
||||
kill "$holder" 2>/dev/null || true
|
||||
wait "$holder" 2>/dev/null || true
|
||||
echo "FAIL: IPC namespace holder did not start" >&2
|
||||
exit 30
|
||||
fi
|
||||
|
||||
exec 9<"/proc/$holder/ns/ipc"
|
||||
kill "$holder"
|
||||
wait "$holder" 2>/dev/null || true
|
||||
|
||||
LATX_AOT=1 LATX_KZT=0 "$emulator" "$guest" &
|
||||
emulator_pid=$!
|
||||
exec 9<&-
|
||||
wait "$emulator_pid" || {
|
||||
ret=$?
|
||||
echo "FAIL: $case_name guest exited with status $ret" >&2
|
||||
exit "$ret"
|
||||
}
|
||||
|
||||
if touch mqueue/MQ2 2>/dev/null; then
|
||||
echo "FAIL: $case_name left the IPC namespace alive" >&2
|
||||
exit 40
|
||||
fi
|
||||
echo "PASS: $case_name released the IPC namespace"
|
||||
' sh "$emulator" "$guest" "$case_name" "$workdir/$case_name"
|
||||
}
|
||||
|
||||
run_invalid_fd_case
|
||||
run_pidfd_case
|
||||
run_case 134217728 explicit-CLONE_NEWIPC
|
||||
run_case 0 inferred-from-fd
|
||||
Loading…
Reference in New Issue