linux-user: retry transient private futex faults - #461
Conversation
| * remains readable across that host-only move, unlike an invalid guest | ||
| * futex address. Check it under mmap_lock to retain genuine EFAULTs. | ||
| */ | ||
| if (qemu_host_page_size <= TARGET_PAGE_SIZE || ret != -TARGET_EFAULT || |
There was a problem hiding this comment.
由于我本地并没有 4k 内核的环境,我稍后重新提交一版再帮忙审查一下。
There was a problem hiding this comment.
新的commit中加了对pressure-vessel的支持,这里应该暂时不需要了
There was a problem hiding this comment.
直接STEAMOS=1 steam可以启动吗
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
有复现环境吗?我目前的运行环境:
系统:aosc 13.3.1
runtime:runtime
steam安装包:官网下载
运行命令:STEAMOS=1 LATX_KZT=0 usr/bin/steam
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
有复现环境吗?我目前的运行环境: 系统:aosc 13.3.1 runtime:runtime steam安装包:官网下载 运行命令:
STEAMOS=1 LATX_KZT=0 usr/bin/steam
Debian 13,没有使用 runtime 而是通过 dpkg --add-architecture 的方式添加 i386 和 amd64 架构安装所需的依赖。
cat > /etc/apt/sources.list.d/trixie.sources << "EOF"
Types: deb
# http://snapshot.debian.org/archive/debian/20260824T000000Z
URIs: http://deb.debian.org/debian
Suites: trixie trixie-updates
Components: main
Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp
Types: deb
# http://snapshot.debian.org/archive/debian-security/20260824T000000Z
URIs: http://deb.debian.org/debian-security
Suites: trixie-security
Components: main
Signed-By: /usr/share/keyrings/debian-archive-keyring.pgp
EOF
dpkg --add-architecture i386
dpkg --add-architecture amd64
# 修改 steam.deb DEBIAN/control 重新打包安装。There was a problem hiding this comment.
可以先合fe9c44cfc7f51e8002454213bc36e631e3b0a347这个commit,后面两个commit先撤掉吧
310b34e to
36f844c
Compare
36f844c to
6c124ec
Compare
665800a to
fe9c44c
Compare
|
感谢修复!我这边测到一个边界问题。 先 mmap 一个文件,再用 问题应该就在 复现程序放下面了,不需要 32 位 libc。 repro.c/* Freestanding i386 regression: no guest libc or multilib headers needed. */
typedef unsigned int u32;
extern int sc(int nr, u32 a, u32 b, u32 c, u32 d, u32 e, u32 f);
__asm__(
".text\n.globl sc\nsc:\n"
"push %ebp; push %edi; push %esi; push %ebx;\n"
"mov 20(%esp), %eax; mov 24(%esp), %ebx;\n"
"mov 28(%esp), %ecx; mov 32(%esp), %edx;\n"
"mov 36(%esp), %esi; mov 40(%esp), %edi;\n"
"mov 44(%esp), %ebp; int $0x80;\n"
"pop %ebx; pop %esi; pop %edi; pop %ebp; ret;\n");
#define PTR(p) ((u32)(p))
static void say(const char *s)
{
u32 n = 0;
while (s[n]) { n++; }
sc(4, 1, PTR(s), n, 0, 0, 0);
}
static void number(int n)
{
char buf[16];
unsigned int v = n < 0 ? -n : n;
unsigned int i = sizeof(buf);
do { buf[--i] = '0' + v % 10; v /= 10; } while (v);
if (n < 0) { buf[--i] = '-'; }
sc(4, 1, PTR(buf + i), sizeof(buf) - i, 0, 0, 0);
}
static void die(const char *s, int ret)
{
say(s); number(ret); say("\n");
sc(1, 2, 0, 0, 0, 0, 0);
__builtin_unreachable();
}
void _start(void)
{
const char path[] = "futex-truncate.data";
const u32 len = 65536;
const unsigned long long zero[2] = {0, 0};
const int syscalls[2] = {240, 422}; /* futex, futex_time64 */
const int ops[2] = {128, 137}; /* PRIVATE WAIT, PRIVATE WAIT_BITSET */
int fd = sc(5, PTR(path), 0xc2, 0600, 0, 0, 0);
if (fd < 0) { die("open: ", fd); }
int ret = sc(10, PTR(path), 0, 0, 0, 0, 0);
if (ret) { die("unlink: ", ret); }
ret = sc(93, fd, len, 0, 0, 0, 0);
if (ret) { die("ftruncate: ", ret); }
u32 p = sc(192, 0, len, 3, 2, fd, 0); /* mmap2, RW, MAP_PRIVATE */
if (p >= (u32)-4095) { die("mmap2: ", p); }
ret = sc(93, fd, 0, 0, 0, 0, 0);
if (ret) { die("truncate zero: ", ret); }
int failed = 0;
/* No userspace access to p: only the kernel reads the futex word. */
for (u32 i = 0; i < 2; i++) {
for (u32 j = 0; j < 2; j++) {
ret = sc(syscalls[i], p, ops[j], 0, PTR(zero), 0, 0xffffffff);
say("syscall="); number(syscalls[i]);
say(" op="); number(ops[j]); say(" result="); number(ret);
say(" expected=-14 "); say(ret == -14 ? "PASS\n" : "FAIL\n");
failed |= ret != -14;
}
}
sc(91, p, len, 0, 0, 0, 0);
sc(6, fd, 0, 0, 0, 0, 0);
sc(1, failed, 0, 0, 0, 0, 0);
__builtin_unreachable();
}在 x86 Linux 上编译: gcc -m32 -nostdlib -static -fno-pie -no-pie -fno-stack-protector \
-fno-builtin -O2 -Wall -Wextra -Werror repro.c -o repro
./repro再把 timeout 15s /path/to/latx-i386 ./repro原生四项都返回 我的想法是保留现在的锁和页检查,在锁内再确认一下这 4 字节能不能读,读到了才改成 参考 diff--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -11061,25 +11061,34 @@
* The i386 lock-instruction interpreter transiently moves a complete
* host page. A private futex in that page can observe the short gap as
* EFAULT, including when host and target pages are both 4 KiB. The guest
- * mapping metadata persists across that host-only move. Check it under
- * mmap_lock so unmapped or unreadable guest futex addresses retain
- * EFAULT. For a mapped private WAIT, return EAGAIN so userspace reloads
- * the futex word and retries.
+ * mapping metadata persists across that host-only move. Taking
+ * mmap_lock waits for relocation to finish. Also probe the actual
+ * word: readable guest metadata can survive truncation of a mapped
+ * file. Only a successful read permits returning EAGAIN.
*/
if (ret != -TARGET_EFAULT || !(op & FUTEX_PRIVATE_FLAG)) {
return ret;
}
base_op = op & FUTEX_CMD_MASK;
- if (!h2g_valid(uaddr)) {
+ if ((base_op != FUTEX_WAIT && base_op != FUTEX_WAIT_BITSET) ||
+ !h2g_valid(uaddr)) {
return ret;
}
mmap_lock();
mapped = (page_get_flags(h2g(uaddr)) & (PAGE_VALID | PAGE_READ)) ==
(PAGE_VALID | PAGE_READ);
+ if (mapped) {
+ uint32_t word;
+ struct iovec local = { .iov_base = &word, .iov_len = sizeof(word) };
+ struct iovec remote = { .iov_base = uaddr, .iov_len = sizeof(word) };
+
+ /* Let the kernel report inaccessible backing memory as an error. */
+ if (process_vm_readv(getpid(), &local, 1, &remote, 1, 0) ==
+ sizeof(word)) {
+ ret = -TARGET_EAGAIN;
+ }
+ }
mmap_unlock();
- if ((base_op == FUTEX_WAIT || base_op == FUTEX_WAIT_BITSET) && mapped) {
- return -TARGET_EAGAIN;
- }
#endif
return ret;
}不过这个改法我还没编译实测, |
Signed-off-by: 吴小白 <296015668@qq.com>
a5f444d to
58ee2dd
Compare
Signed-off-by: 吴小白 <296015668@qq.com>
58ee2dd to
ce77b51
Compare

Fixes #87