[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#880582: apt: apt update hangs under qemu-user when host and target architecture are 64-bit



Control: reassign -1 qemu-user 1:2.10.0+dfsg-2
Control: forwarded -1 https://bugs.launchpad.net/qemu/+bug/1726394
Control: severity -1 normal
Control: retitle -1 qemu-user: should not passthrough prctl(PR_SET_SECCOMP)
Control: affects -1 src:apt
Control: tags -1 patch

Hi,

On 02/11/17 15:04, Julian Andres Klode wrote:
> On Thu, Nov 02, 2017 at 02:49:07PM +0000, James Cowgill wrote:
>> Package: apt
>> Version: 1.6~alpha3
>> Severity: important
>>
>> Hi,
>>
>> I noticed that with apt 1.6, running apt-get update hangs in a mips64el
>> chroot running on an amd64 host using qemu-user-static. I can also
>> reproduce this with an arm64 target so I suspect this affects all 64-bit
>> architectures running on amd64.
>>
>> It outputs this before hanging (on mips64el):
>>> # apt-get update
>>> 0% [Working]qemu: Unsupported syscall: 5312
>>> 0% [Working]
>>
>> Syscall 5312 is "seccomp".
>>
>> If I run qemu-user with the -strace option, I see that the http method
>> calls the seccomp syscall which fails with ENOSYS (since it's not
>> supported in qemu). Then, libseccomp calls the old prctl(PR_SET_SECCOMP)
>> syscall which succeeds. I think in this case a valid seccomp filter is
>> installed, but for the wrong architecture. This results in the calling
>> thread being immediately killed when a syscall is later executed.
>>
>> The apt changelog mentions checking for EFAULT in case apt is started
>> inside QEMU. I think this only works by chance on 32-bit targets because
>> they would pass a truncated pointer to the real prctl which the kernel
>> would usually reject as an invalid address.
> 
> Sure, and there's nothing I can do about it. QEMU needs to be fixed in
> 
>    https://bugs.launchpad.net/qemu/+bug/1726394
> 
> Nobody is reacting there.

OK. I didn't realize you already knew about this bug. I sent a patch
upstream to fix it (attached). 

> Unless you know how to reliably detect qemu-user, then that's easy
> - we could even just install filters for the host architecture.
> 
>>
>> I think the hanging is caused by the http method having two threads.
>> When an invalid syscall is executed under seccomp, only the calling
>> thread is killed. Since the http method is running two threads, the
>> other is left running and hangs. In turn this causes the parent apt
>> process to wait for the http method to exit which will never happen.
> 
> Are there two threads? I only know one. Anyway, this is supposed to
> call a signal handler which calls _exit, but apparently the signal
> handler was not called, or rather, presumably write(2) and _exit(2)
> were blocked by the invalid seccomp filter.

I ran strace on the method both inside and outside QEMU and the thread
only appears when run in qemu-user so it must be a QEMU thing. In that
case, if the original bug is fixed, this doesn't matter too much.

Thanks,
James
From 4d7ff6dc854fe7d24ee2aeff39b2973f87012fdd Mon Sep 17 00:00:00 2001
From: James Cowgill <james.cowgill@mips.com>
Date: Fri, 3 Nov 2017 11:06:19 +0000
Subject: [PATCH] linux-user: return EINVAL from prctl(PR_*_SECCOMP)

If an application tries to install a seccomp filter using
prctl(PR_SET_SECCOMP), the filter is likely for the target instead of the host
architecture. This will probably cause qemu to be immediately killed when it
executes another syscall.

Prevent this from happening by returning EINVAL from both seccomp prctl
calls. This is the error returned by the kernel when seccomp support is
disabled.

Fixes: https://bugs.launchpad.net/qemu/+bug/1726394
Signed-off-by: James Cowgill <james.cowgill@mips.com>
---
 linux-user/syscall.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index d4497dec5d..43cd5fb2bb 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -10482,6 +10482,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
             break;
         }
 #endif
+        case PR_GET_SECCOMP:
+        case PR_SET_SECCOMP:
+            ret = -TARGET_EINVAL;
+            break;
         default:
             /* Most prctl options have no pointer arguments */
             ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5));
-- 
2.15.0

Attachment: signature.asc
Description: OpenPGP digital signature


Reply to: