termux / proot

An chroot-like implementation using ptrace.
https://wiki.termux.com/wiki/PRoot
Other
745 stars 161 forks source link

fork: Function not implemented on Chromebook #237

Closed corbinlc closed 1 year ago

corbinlc commented 2 years ago

It looks like running PRoot on HP Chromebooks is running into a unique problem. Specifically, it is getting an unrecoverable SIGSYS after calling the fork system call. The devices effected are x86_64 devices. Note, this is occurring in UserLAnd, but should equally effect Termux. I will see if I can repeat it there later. @michalbednarski do you have any thoughts on this?

Here is a short PRoot log. The only interesting part is right at the end:

proot warning: can't sanitize binding "/storage/emulated/0/Documents": No such file or directory
proot warning: can't sanitize binding "/data/user/0/tech.ula/files/1/tmp": No such file or directory
proot info: Checking for f2fs case sensitivity bug
proot info: f2fs bug not present on device
proot info: binding = /data/data/tech.ula/files/support/version:/proc/version
proot info: binding = /data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_uptime.so:/proc/uptime
proot info: binding = /data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_stat4.so:/proc/stat
proot info: binding = /data/data/tech.ula/files/Intents:/Intents
proot info: binding = /storage/emulated/0/DCIM:/DCIM
proot info: binding = /storage/emulated/0/Movies:/Videos
proot info: binding = /storage/emulated/0/Pictures:/Pictures
proot info: binding = /storage/emulated/0/Music:/Music
proot info: binding = /storage/emulated/0/Download:/Downloads
proot info: binding = /storage/emulated/0:/sdcard
proot info: binding = /storage/emulated/0/Android/data/tech.ula/files/storage:/storage/internal
proot info: binding = /data/data/tech.ula/files/support:/support/common
proot info: binding = /data/data/tech.ula/files/1/support/ld.so.preload:/etc/ld.so.preload
proot info: binding = /data/data/tech.ula/files/1/support/userland_profile.sh:/etc/profile.d/userland_profile.sh
proot info: binding = /data/data/tech.ula/files/1/support/nosudo:/usr/local/bin/sudo
proot info: binding = /data/data/tech.ula/files/1/support:/support
proot info: binding = /:/host-rootfs
proot info: binding = /proc/3905/mounts:/etc/mtab
proot info: binding = /mnt
proot info: binding = /data
proot info: binding = /proc
proot info: binding = /dev
proot info: binding = /sys
proot info: binding = /data/data/tech.ula/files/1:/
proot info: vpid 1: translate("/" + "/support/common/extractFilesystem.sh")
proot info: vpid 1:          -> "/data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_extractFilesystem.sh.so"
proot info: vpid 1: translate("/" + "/support/common/extractFilesystem.sh")
proot info: vpid 1:          -> "/data/data/tech.ula/files/support/extractFilesystem.sh"
proot info: glue rootfs = /data/data/tech.ula/files/1/support/proot-3905-he5aNU
proot info: exe = /support/common/extractFilesystem.sh
proot info: argv = /support/common/extractFilesystem.sh
proot info: initial cwd = /data/data/tech.ula/files
proot info: verbose level = 9
proot info: pid 3905: access to "/proc/3905/fd" (fd 3) won't be translated until closed
proot info: vpid 1: sysenter start: prctl(0x26, 0x1, 0x0, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffd8fe48948, 0]
proot info: vpid 1: sysenter end: prctl(0x26, 0x1, 0x0, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffd8fe48948, 0]
proot info: vpid 1: sysexit start: prctl(0x26, 0x1, 0x0, 0x0, 0x0, 0x0) = 0x0 [0x7ffd8fe48948, 0]
proot info: vpid 1: sysexit end: prctl(0x26, 0x1, 0x0, 0x0, 0x0, 0x0) = 0x0 [0x7ffd8fe48948, 0]
proot info: vpid 1: sysenter start: prctl(0x16, 0x2, 0x7ffd8fe48990, 0x7c651930069a, 0x0, 0x0) = 0xffffffffffffffda [0x7ffd8fe48948, 0]
proot info: vpid 1: sysenter end: prctl(0x16, 0x2, 0x7ffd8fe48990, 0x7c651930069a, 0x0, 0x0) = 0xffffffffffffffda [0x7ffd8fe48948, 0]
proot info: vpid 1: sysexit start: prctl(0x16, 0x2, 0x7ffd8fe48990, 0x7c651930069a, 0x0, 0x0) = 0x0 [0x7ffd8fe48948, 0]
proot info: vpid 1: sysexit end: prctl(0x16, 0x2, 0x7ffd8fe48990, 0x7c651930069a, 0x0, 0x0) = 0x0 [0x7ffd8fe48948, 0]
proot info: ptrace acceleration (seccomp mode 2, old syscall order) enabled
proot info: vpid 1: sysenter start: execve(0x7c6497aa3b40, 0x7ffd8fe4ad20, 0x7ffd8fe4ad30, 0x7c651930069a, 0xffffffffffffffb0, 0x0) = 0xffffffffffffffda [0x7ffd8fe489a8, 0]
proot info: vpid 1: translate("/" + "/support/common/extractFilesystem.sh")
proot info: vpid 1:          -> "/data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_extractFilesystem.sh.so"
proot info: vpid 1: translate("/" + "/support/common/busybox_static")
proot info: vpid 1:          -> "/data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_busybox_static.so"
proot info: vpid 1: translate("/" + "/support/common/busybox_static")
proot info: vpid 1:          -> "/data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_busybox_static.so"
proot info: vpid 1: translate("/" + "/support/common/extractFilesystem.sh")
proot info: vpid 1:          -> "/data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_extractFilesystem.sh.so"
proot info: vpid 1: sysenter end: execve(0x7ffd8fe48896, 0x7ffd8fe488c1, 0x7ffd8fe4ad30, 0x7c651930069a, 0xffffffffffffffb0, 0x0) = 0xffffffffffffffda [0x7ffd8fe48896, 0]
proot info: vpid 1: sysexit start: execve(0x0, 0x0, 0x0, 0x0, 0x0, 0x0) = 0x0 [0x7ffc058fae80, 0]
proot info: vpid 1: sysexit end: execve(0x7ffc058facf8, 0x0, 0x0, 0x0, 0x0, 0x0) = 0x0 [0x7ffc058facf8, 0]
proot info: vpid 1: sysenter start: open(0x7ffc058fae38, 0x0, 0x0, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058facc8, 0]
proot info: vpid 1: translate("/" + "/support/common/busybox_static")
proot info: vpid 1:          -> "/data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_busybox_static.so"
proot info: vpid 1: sysenter end: open(0x7ffc058fabfb, 0x0, 0x0, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058facc8, 0]
proot info: vpid 1: sysenter start: prctl(0xf, 0x7ffc058fae67, 0x0, 0x32, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058facc8, 0]
proot info: vpid 1: sysenter end: prctl(0xf, 0x7ffc058fae67, 0x0, 0x32, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058facc8, 0]
proot info: vpid 1: sysenter start: getuid(0x7ffc058fb5c1, 0x2f, 0x7ffc058fb5d0, 0x1, 0xfefefefefefefeff, 0x0) = 0xffffffffffffffda [0x7ffc058fae28, 0]
proot info: vpid 1: sysenter end: getuid(0x7ffc058fb5c1, 0x2f, 0x7ffc058fb5d0, 0x1, 0xfefefefefefefeff, 0x0) = 0xffffffffffffffda [0x7ffc058fae28, 0]
proot info: vpid 1: sysexit start: getuid(0x7ffc058fb5c1, 0x2f, 0x7ffc058fb5d0, 0x1, 0xfefefefefefefeff, 0x0) = 0x2751 [0x7ffc058fae28, 0]
proot info: vpid 1: sysexit end: getuid(0x7ffc058fb5c1, 0x2f, 0x7ffc058fb5d0, 0x1, 0xfefefefefefefeff, 0x0) = 0x0 [0x7ffc058fae28, 0]
proot info: vpid 1: sysenter start: brk(0x0, 0x19, 0x1000, 0x1, 0xfefefefefefefeff, 0x0) = 0xffffffffffffffda [0x7ffc058fabc0, 0]
proot info: vpid 1: sysenter end: mmap(0x5000000ec000, 0x1000, 0x3, 0x22, 0xffffffffffffffff, 0x0) = 0xffffffffffffffda [0x7ffc058fabc0, 0]
proot info: vpid 1: sysexit start: mmap(0x5000000ec000, 0x1000, 0x3, 0x22, 0xffffffffffffffff, 0x0) = 0x5000000ec000 [0x7ffc058fabc0, 0]
proot info: vpid 1: sysexit end: brk(0x0, 0x19, 0x1000, 0x1, 0xfefefefefefefeff, 0x0) = 0x5000000ed000 [0x7ffc058fabc0, 0]
proot info: vpid 1: sysenter start: brk(0x5000000ee000, 0x5000000ed000, 0x1000, 0x1, 0xfefefefefefefeff, 0x0) = 0xffffffffffffffda [0x7ffc058fabc0, 0]
proot info: vpid 1: sysenter end: mremap(0x5000000ec000, 0x1000, 0x2000, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058fabc0, 0]
proot info: vpid 1: sysexit start: mremap(0x5000000ec000, 0x1000, 0x2000, 0x0, 0x0, 0x0) = 0x5000000ec000 [0x7ffc058fabc0, 0]
proot info: vpid 1: sysexit end: brk(0x5000000ee000, 0x5000000ed000, 0x1000, 0x1, 0xfefefefefefefeff, 0x0) = 0x5000000ee000 [0x7ffc058fabc0, 0]
proot info: vpid 1: sysenter start: brk(0x5000000ff000, 0x5000000ee000, 0x11000, 0x1, 0x5000000eda20, 0x0) = 0xffffffffffffffda [0x7ffc058fabb0, 0]
proot info: vpid 1: sysenter end: mremap(0x5000000ec000, 0x2000, 0x13000, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058fabb0, 0]
proot info: vpid 1: sysexit start: mremap(0x5000000ec000, 0x2000, 0x13000, 0x0, 0x0, 0x0) = 0x5000000ec000 [0x7ffc058fabb0, 0]
proot info: vpid 1: sysexit end: brk(0x5000000ff000, 0x5000000ee000, 0x11000, 0x1, 0x5000000eda20, 0x0) = 0x5000000ff000 [0x7ffc058fabb0, 0]
proot info: vpid 1: sysenter start: uname(0x7ffc058faac0, 0x5000000d887e, 0x48, 0x5000000d887e, 0x5000000fe060, 0x0) = 0xffffffffffffffda [0x7ffc058faa20, 0]
proot info: vpid 1: sysenter end: uname(0x7ffc058faac0, 0x5000000d887e, 0x48, 0x5000000d887e, 0x5000000fe060, 0x0) = 0xffffffffffffffda [0x7ffc058faa20, 0]
proot info: vpid 1: sysexit start: uname(0x7ffc058faac0, 0x5000000d887e, 0x48, 0x5000000d887e, 0x5000000fe060, 0x0) = 0x0 [0x7ffc058faa20, 0]
proot info: vpid 1: sysexit end: uname(0x7ffc058faac0, 0x5000000d887e, 0x48, 0x5000000d887e, 0x5000000fe060, 0x0) = 0x0 [0x7ffc058faa20, 0]
proot info: vpid 1: seccomp SIGSYS: stat(0x7ffc058fbcba, 0x7ffc058faa30, 0x3d, 0x5000000d858b, 0x5000000ed820, 0x5000000fe3e0) = 0x4 [0x7ffc058faa20, 0]
proot info: vpid 1: sysenter start: newfstatat(0xffffffffffffff9c, 0x7ffc058fbcba, 0x7ffc058faa30, 0x0, 0x5000000ed820, 0x5000000fe3e0) = 0xffffffffffffffda [0x7ffc058faa20, 0]
proot info: vpid 1: translate("/" + "/data/data/tech.ula/files")
proot info: vpid 1:          -> "/data/data/tech.ula/files"
proot info: vpid 1: sysenter end: newfstatat(0xffffffffffffff9c, 0x7ffc058fa986, 0x7ffc058faa30, 0x0, 0x5000000ed820, 0x5000000fe3e0) = 0xffffffffffffffda [0x7ffc058fa986, 0]
proot info: vpid 1: sysexit start: newfstatat(0xffffffffffffff9c, 0x7ffc058fa986, 0x7ffc058faa30, 0x0, 0x5000000ed820, 0x5000000fe3e0) = 0x0 [0x7ffc058fa986, 0]
proot info: vpid 1: sysexit end: void(0x7ffc058fbcba, 0x7ffc058faa30, 0x3d, 0x5000000d858b, 0x5000000ed820, 0x5000000fe3e0) = 0x0 [0x7ffc058faa20, 0]
proot info: vpid 1: seccomp SIGSYS: stat(0x5000000d8504, 0x7ffc058faac0, 0x7ffc058fbcba, 0x5000000d858b, 0x5000000ed820, 0x5000000fe3e0) = 0x4 [0x7ffc058faa20, 0]
proot info: vpid 1: sysenter start: newfstatat(0xffffffffffffff9c, 0x5000000d8504, 0x7ffc058faac0, 0x0, 0x5000000ed820, 0x5000000fe3e0) = 0xffffffffffffffda [0x7ffc058faa20, 0]
proot info: vpid 1: translate("/data/data/tech.ula/files" + ".")
proot info: vpid 1:          -> "/data/data/tech.ula/files/."
proot info: vpid 1: sysenter end: newfstatat(0xffffffffffffff9c, 0x7ffc058fa984, 0x7ffc058faac0, 0x0, 0x5000000ed820, 0x5000000fe3e0) = 0xffffffffffffffda [0x7ffc058fa984, 0]
proot info: vpid 1: sysexit start: newfstatat(0xffffffffffffff9c, 0x7ffc058fa984, 0x7ffc058faac0, 0x0, 0x5000000ed820, 0x5000000fe3e0) = 0x0 [0x7ffc058fa984, 0]
proot info: vpid 1: sysexit end: void(0x5000000d8504, 0x7ffc058faac0, 0x7ffc058fbcba, 0x5000000d858b, 0x5000000ed820, 0x5000000fe3e0) = 0x0 [0x7ffc058faa20, 0]
proot info: vpid 1: sysenter start: open(0x7ffc058fb5e3, 0x80000, 0x0, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058fabc8, 0]
proot info: vpid 1: translate("/" + "/support/common/extractFilesystem.sh")
proot info: vpid 1:          -> "/data/app/tech.ula-YMZFWHGr4hMZHCIct9WKaw==/lib/x86_64/lib_extractFilesystem.sh.so"
proot info: vpid 1: sysenter end: open(0x7ffc058faaf5, 0x80000, 0x0, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058fabc8, 0]
proot info: vpid 1: sysenter start: wait4(0xffffffffffffffff, 0x7ffc058fa90c, 0x1, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058fa8d8, 0]
proot info: vpid 1: sysenter end: wait4(0xffffffffffffffff, 0x7ffc058fa90c, 0x1, 0x0, 0x0, 0x0) = 0xffffffffffffffda [0x7ffc058fa8d8, 0]
proot info: vpid 1: sysexit start: wait4(0xffffffffffffffff, 0x7ffc058fa90c, 0x1, 0x0, 0x0, 0x0) = 0xfffffffffffffff6 [0x7ffc058fa8d8, 0]
proot info: vpid 1: sysexit end: wait4(0xffffffffffffffff, 0x7ffc058fa90c, 0x1, 0x0, 0x0, 0x0) = 0xfffffffffffffff6 [0x7ffc058fa8d8, 0]
proot info: vpid 1: seccomp SIGSYS: stat(0x5000000fe998, 0x7ffc058fa6c8, 0xffffffff, 0x0, 0xfefefefefefefeff, 0x0) = 0x4 [0x7ffc058fa6b0, 0]
proot info: vpid 1: sysenter start: newfstatat(0xffffffffffffff9c, 0x5000000fe998, 0x7ffc058fa6c8, 0x0, 0xfefefefefefefeff, 0x0) = 0xffffffffffffffda [0x7ffc058fa6b0, 0]
proot info: vpid 1: translate("/" + "/support/rootfs.tar.gz")
proot info: vpid 1:          -> "/data/data/tech.ula/files/1/support/rootfs.tar.gz"
proot info: vpid 1: sysenter end: newfstatat(0xffffffffffffff9c, 0x7ffc058fa5fe, 0x7ffc058fa6c8, 0x0, 0xfefefefefefefeff, 0x0) = 0xffffffffffffffda [0x7ffc058fa5fe, 0]
proot info: vpid 1: sysexit start: newfstatat(0xffffffffffffff9c, 0x7ffc058fa5fe, 0x7ffc058fa6c8, 0x0, 0xfefefefefefefeff, 0x0) = 0x0 [0x7ffc058fa5fe, 0]
proot info: vpid 1: sysexit end: void(0x5000000fe998, 0x7ffc058fa6c8, 0xffffffff, 0x0, 0xfefefefefefefeff, 0x0) = 0x0 [0x7ffc058fa6b0, 0]
proot info: vpid 1: seccomp SIGSYS: fork(0x0, 0x5000000c5df8, 0x7ffc058fa970, 0x8, 0x101010101010101, 0x8080808080808080) = 0x39 [0x7ffc058fa970, 0]
proot info: Setting result after SIGSYS to 0xffffffffffffffda
/support/common/extractFilesystem.sh: line 8: can't fork: Function not implemented
proot info: vpid 1: exited with status 2
corbinlc commented 2 years ago

I wonder if the SIGSYS needs to be intercepted and then PR_clone used instead. I have a feeling that PR_fork is on its way out.

corbinlc commented 2 years ago

This is now some glibc implementations of fork are handled. With the last 5 arguments listed here, clone will act like fork.


  INLINE_SYSCALL (clone, 5,                           \
          CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0,     \
          NULL, NULL, &THREAD_SELF->tid)```
michalbednarski commented 2 years ago

Seems that you're right.

Something to test that under Termux would be nice (so far most programs didn't use fork syscall and instead used clone; vfork also seems to be allowed (and also vfork is another clone with preset arguments))

Note that fork sets flags to just SIGCHLD

clone has different argument orders on different architectures, but it seems to not be problem in this case as across architectures supported by proot only last two arguments may be swapped and we want set both to zero anyway (You can find architectures which swap arguments by running grep -r 'select CLONE_BACKWARDS' arch in Linux source tree)

So looks like answer would be (I haven't tested this code) :

    case PR_fork:
        set_sysnum(tracee, PR_clone);
        poke_reg(tracee, SYSARG_1, SIGCHLD);
        poke_reg(tracee, SYSARG_2, 0);
        poke_reg(tracee, SYSARG_3, 0);
        poke_reg(tracee, SYSARG_4, 0);
        poke_reg(tracee, SYSARG_5, 0);
        restart_syscall_after_seccomp(tracee);
        break;
corbinlc commented 2 years ago

Thanks. That works well. I came to the same conclusion that you don't need to worry about the arch dependent swapping of variables and that you can set args 2-5 to 0, but I am glad you have the same thoughts. It is interesting that the code in glibc is more complex and set additional arguments when fork is called. The trickier one is alarm is also not allowed anymore. This will probably require setitimer. That is a little more complex as the result at sysexit will have to be modified. This is what glibc does:

unsigned int 
alarm (unsigned int seconds)
{
  struct itimerval old, new;
  unsigned int retval;
 
  new.it_interval.tv_usec = 0;
  new.it_interval.tv_sec = 0;
  new.it_value.tv_usec = 0;
  new.it_value.tv_sec = (long int) seconds;
  if (__setitimer (ITIMER_REAL, &new, &old) < 0)
    return 0;
 
  retval = old.it_value.tv_sec;
  /* Round to the nearest second, but never report zero seconds when the alarm is still set.  */
  if (old.it_value.tv_usec >= 500000
   (retval == 0 && old.it_value.tv_usec > 0))
    ++retval;
    return retval;
  }

So a solution for this will have to allocate memory for the struct being used and intercept at sysexit as well.

corbinlc commented 1 year ago

This is what I put in seccomp.c:

        case PR_alarm:
        {
                unsigned int seconds;
                struct itimerval old, new;
                word_t old_arg = 0;
                word_t new_arg = 0;

                seconds = peek_reg(tracee, CURRENT, SYSARG_1);

                old.it_interval.tv_usec = 0;
                old.it_interval.tv_sec = 0;
                old.it_value.tv_usec = 0;
                old.it_value.tv_sec = 0;

                old_arg = alloc_mem(tracee, sizeof(old));
                if(write_data(tracee, old_arg, &old, sizeof(old))) {
                        set_result_after_seccomp(tracee, -EFAULT);
                        break;
                }

                new.it_interval.tv_usec = 0;
                new.it_interval.tv_sec = 0;
                new.it_value.tv_usec = 0;
                new.it_value.tv_sec = (long int) seconds;

                new_arg = alloc_mem(tracee, sizeof(new));
                if(write_data(tracee, new_arg, &new, sizeof(new))) {
                        set_result_after_seccomp(tracee, -EFAULT);
                        break;
                }

                set_sysnum(tracee, PR_setitimer);
                poke_reg(tracee, SYSARG_1, ITIMER_REAL);
                poke_reg(tracee, SYSARG_2, new_arg);
                poke_reg(tracee, SYSARG_3, old_arg);
                restart_syscall_after_seccomp(tracee);
                break;
        }

@michalbednarski Where is the best place to handled modifying the return value for things restarted by seccomp.c?

michalbednarski commented 1 year ago

I'm afraid there is no dedicated place for that, so you'd have to put it in translate_syscall_exit (Note that switch (syscall_number) is on rewritten syscall, so add case PR_setitimer: and inside that add condition on e.g. tracee->restore_original_regs_after_seccomp_event and/or custom field in typedef struct { ... } Tracee)

Another issue here is that struct itimerval varies between 32 and 64-bit

corbinlc commented 1 year ago

@michalbednarski Does this look like a good first attempt for the translate_syscall_exit modification?

case PR_setitimer: {
                struct itimerval old;

                if (!tracee->restore_original_regs_after_seccomp_event)
                        goto end;

                if (syscall_result < 0) {
                        status = 0;
                        break;
                }

                status = read_data(tracee, &old, peek_reg(tracee, ORIGINAL, SYSARG_3), sizeof(old));
                if (status < 0)
                        break;

                status = old.it_value.tv_sec;
                /* Round to the nearest second, but never report zero seconds when the alarm is still set.  */
                if (old.it_value.tv_usec >= 500000 ||
                    (status == 0 && old.it_value.tv_usec > 0))
                        ++status;
                break;
        }

Note, status gets written into the SYSARG_RESULT at the end of the case statement. Also, what is your concern about the different between 32 and 64 bit usage of the struct itimerval? I didn't see any issue after look at the definition. Thanks.

michalbednarski commented 1 year ago

In my test this worked okay (except 32-on-64 mode, I'm including patched version below)

Differences between 32 and 64 bit itimerval, as gathered by using gdb scripting on AOSP build

$ gdb --batch -x itimerval_info.py out/target/product/emulator_x86_64/symbols/apex/com.android.runtime/lib/bionic/libc.so 
struct itimerval { // sizeof=16
  timeval it_interval; // pos=0.0 size=8
  timeval it_value; // pos=8.0 size=8
}
struct timeval { // sizeof=8
  __kernel_old_time_t tv_sec; // pos=0.0 size=4
  __kernel_suseconds_t tv_usec; // pos=4.0 size=4
}
$ gdb --batch -x itimerval_info.py out/target/product/emulator_x86_64/symbols/apex/com.android.runtime/lib64/bionic/libc.so 
struct itimerval { // sizeof=32
  timeval it_interval; // pos=0.0 size=16
  timeval it_value; // pos=16.0 size=16
}
struct timeval { // sizeof=16
  __kernel_old_time_t tv_sec; // pos=0.0 size=8
  __kernel_suseconds_t tv_usec; // pos=8.0 size=8
}
$ cat itimerval_info.py 
def print_type_info(name):
    typ = gdb.lookup_type(name)
    print(name + " { // sizeof=" + str(typ.sizeof))
    for field in typ.fields():
        print("  " + field.type.name + " " + field.name + "; // pos=" + str(field.bitpos/8) + " size=" + str(field.type.sizeof))
    print("}")

print_type_info("struct itimerval")
print_type_info("struct timeval")

Version supporting 32-on-64 (mostly based on your patches):

diff --git a/src/syscall/exit.c b/src/syscall/exit.c
index b3c88b2..d487332 100644
--- a/src/syscall/exit.c
+++ b/src/syscall/exit.c
@@ -536,6 +536,36 @@ void translate_syscall_exit(Tracee *tracee)
        status = handle_statx_syscall(tracee, false);
        break;

+   case PR_setitimer: {
+       struct itimerval old;
+
+       if (!tracee->restore_original_regs_after_seccomp_event)
+               goto end;
+
+       if (syscall_result < 0) {
+               status = 0;
+               break;
+       }
+
+       status = read_data(tracee, &old, peek_reg(tracee, ORIGINAL, SYSARG_3), sizeof(old));
+       if (status < 0)
+               break;
+
+       if (is_32on64_mode(tracee)) {
+           uint32_t sec = ((uint32_t*) &old)[2];
+           uint32_t usec = ((uint32_t*) &old)[3];
+           old.it_value.tv_sec = sec;
+           old.it_value.tv_usec = usec;
+       }
+
+       status = old.it_value.tv_sec;
+       /* Round to the nearest second, but never report zero seconds when the alarm is still set.  */
+       if (old.it_value.tv_usec >= 500000 ||
+           (status == 0 && old.it_value.tv_usec > 0))
+               ++status;
+       break;
+   }
+
    default:
        goto end;
    }
diff --git a/src/tracee/seccomp.c b/src/tracee/seccomp.c
index 715cfe6..6c758c5 100644
--- a/src/tracee/seccomp.c
+++ b/src/tracee/seccomp.c
@@ -408,6 +408,60 @@ static int handle_seccomp_event_common(Tracee *tracee)
        restart_syscall_after_seccomp(tracee);
        break;

+   case PR_fork:
+       set_sysnum(tracee, PR_clone);
+       poke_reg(tracee, SYSARG_1, SIGCHLD);
+       poke_reg(tracee, SYSARG_2, 0);
+       poke_reg(tracee, SYSARG_3, 0);
+       poke_reg(tracee, SYSARG_4, 0);
+       poke_reg(tracee, SYSARG_5, 0);
+       restart_syscall_after_seccomp(tracee);
+       break;
+
+   case PR_alarm:
+   {
+           unsigned int seconds;
+           struct itimerval old, new;
+           word_t old_arg = 0;
+           word_t new_arg = 0;
+
+           seconds = peek_reg(tracee, CURRENT, SYSARG_1);
+
+           old.it_interval.tv_usec = 0;
+           old.it_interval.tv_sec = 0;
+           old.it_value.tv_usec = 0;
+           old.it_value.tv_sec = 0;
+
+           old_arg = alloc_mem(tracee, sizeof(old));
+           if(write_data(tracee, old_arg, &old, sizeof(old))) {
+                   set_result_after_seccomp(tracee, -EFAULT);
+                   break;
+           }
+
+           new.it_interval.tv_usec = 0;
+           new.it_interval.tv_sec = 0;
+           new.it_value.tv_usec = 0;
+           new.it_value.tv_sec = (long int) seconds;
+
+           if (is_32on64_mode(tracee)) {
+               ((uint32_t*) &new)[2] = seconds;
+               new.it_value.tv_sec = 0;
+           }
+
+           new_arg = alloc_mem(tracee, sizeof(new));
+           if(write_data(tracee, new_arg, &new, sizeof(new))) {
+                   set_result_after_seccomp(tracee, -EFAULT);
+                   break;
+           }
+
+           set_sysnum(tracee, PR_setitimer);
+           poke_reg(tracee, SYSARG_1, ITIMER_REAL);
+           poke_reg(tracee, SYSARG_2, new_arg);
+           poke_reg(tracee, SYSARG_3, old_arg);
+           restart_syscall_after_seccomp(tracee);
+           break;
+   }
+
    case PR_access:
        set_sysnum(tracee, PR_faccessat);
        poke_reg(tracee, SYSARG_4, 0);

(In theory my fix violates strict aliasing, but currently appears to work)

corbinlc commented 1 year ago

This all seems to work. I see one more issue, but I don't think it is related.

Btw... I didn't see an easy way to try this with Termux (had to keep using UserLAnd for testing) because F-droid is not available for Chromebooks. I probably could side load it, but haven't tried working around it yet.

corbinlc commented 1 year ago

I found no more issues with this.