Giter Club home page Giter Club logo

psp2cldr's People

Contributors

chen-charles avatar

Stargazers

 avatar  avatar

Watchers

 avatar

psp2cldr's Issues

valgrind(arm-linux): signal handler receives ucontext with empty cpsr

first issue

master@13427e2ae745f9c5a665174b245d6dcebcf159d9
image

Suspected to be caused by coregrind/m_sigframe/sigframe-arm-linux.c:synth_ucontext not passing CPSR at all.
x86-linux does

   sc->eflags = LibVEX_GuestX86_get_eflags(&tst->arch.vex);

so I suppose we could probably do something similar, probably,

   sc->arm_cpsr = LibVEX_GuestARM_get_cpsr(&tst->arch.vex);

to at least observe a valid cpsr register.

suspected second issue

There is probably something else wrong regarding how valgrind is implementing signals. According to this msg from qemu-devel, arm_pc should actually always have LSB clear and use arm_cpsr.T to indicate the thumb state. Currently, valgrind doesn't even process CPSR at all...

According to DDI0406C_d_armv7ar_arm B1.8.10 Exception return, returning from an exception will use CPSR to determine the target instruction set and ignores the corresponding bits that doesn't align with the mode selected by CPSR (such behavior has been deprecated as well).

The linux kernel doesn't really do anything about PC and CPSR in uc (arch/arm/kernel/signal.c:sys_rt_sigreturn), it just takes them as is. This likely indicates valgrind is the one at fault here.

I can see why valgrind doesn't care about CPSR, it's because valgrind doesn't use CPSR as a register, but more of as a state tracker. The actual thumb bit information is directly encoded in Vex's PC (guest_R15T).

Suspecting this worked in the past because if the user is not changing PC in ucontext, they would be returning the same PC value, which would actually have the LSB set/unset accordingly.

// compile with `gcc a.c`

#include <assert.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

#define TESTBIT(var, pos) ((var) & ((uintptr_t)1 << (pos)))
#define SETBIT(var, pos) ((var) | ((uintptr_t)1 << (pos)))
#define CLEARBIT(var, pos) ((var) & ~((uintptr_t)1 << (pos)))

volatile sig_atomic_t count = 0;
uintptr_t ptr;
int pagesize;

void sigill_handler(int sig, siginfo_t *info, void *ucontext)
{
    ucontext_t *ctx = (ucontext_t *)(ucontext);

    if (count == 0)
    {
        count = 1;

        // On linux, this will goto SIGILL, because the processor will decide based on CPSR.T, not PC's LSB.
        // see DDI0406C_d_armv7ar_arm B1.8.10 Exception return
        // https://developer.arm.com/documentation/ddi0406/cd/?lang=en

        // On qemu, anything could happen, because strictly speaking, ARM stated that,
        // "ARM deprecates any dependence on the requirements that the hardware ignores bits of the address."
        // also see https://lists.gnu.org/archive/html/qemu-devel/2021-04/msg02211.html

        // On valgrind, we would head to Thumb and SEGV.

        ctx->uc_mcontext.arm_pc = ptr + pagesize;
        ctx->uc_mcontext.arm_pc = SETBIT(ctx->uc_mcontext.arm_pc, 0);
        ctx->uc_mcontext.arm_cpsr = CLEARBIT(ctx->uc_mcontext.arm_cpsr, 5);

        // alternatively, on qemu and linux, if we do the followings,

        // ctx->uc_mcontext.arm_pc = ptr + pagesize;
        // ctx->uc_mcontext.arm_cpsr = SETBIT(ctx->uc_mcontext.arm_cpsr, 5);

        // we will see SEGV because we would head to Thumb
        // it's probably unfair to mention valgrind's behavior because valgrind ignores cpsr altogether
    }
    else
    {
        // make sure we landed because we were on an ARM instruction
        assert(TESTBIT(ctx->uc_mcontext.arm_pc, 0) == 0);
        assert(ctx->uc_mcontext.arm_pc == ptr + pagesize);
        _exit(0);
    }
}

int main(int argc, char *argv[])
{
    /**
     * ARM: (SIGILL)
     * udf #0x44
     *
     * THUMB: (SEGV if we protect the page ahead of us)
     * lsls r4, r6, #0x13
     * b 0xffffffe6
     */
    static const uint32_t INSTR_UDF44_ARM = 0xe7f004f0;

    pagesize = sysconf(_SC_PAGE_SIZE);
    assert(pagesize != -1);

    ptr = (uintptr_t)mmap(NULL, 2 * pagesize, PROT_EXEC | PROT_READ | PROT_WRITE,
                          MAP_ANONYMOUS | MAP_POPULATE | MAP_PRIVATE, -1, 0);
    assert(TESTBIT(ptr, 0) == 0);

    *(uint32_t *)(ptr + pagesize) = INSTR_UDF44_ARM;
    assert(mprotect((void *)ptr, pagesize, PROT_NONE) != -1);

    struct sigaction action, oldaction;
    sigfillset(&action.sa_mask);
    action.sa_flags = SA_SIGINFO;
    action.sa_sigaction = sigill_handler;
    assert(sigaction(SIGILL, &action, &oldaction) == 0);

    __asm__("udf #0xff");

    return 0;
}

next steps

Unfortunately, valgrind's sigframe implementation is incomplete/problematic in certain ways (coregrind/m_sigframe/sigframe-arm-linux.c:VG_(sigframe_destroy)). Even x86-linux doesn't know how to restore eflags and fp states from uc.

Simply adding a line to pass cpsr in uc may/should resolve the first issue here.
Probably an additional block to interpret CPSR returned from the signal handler might be necessary to align with the standard behaviors.

memory leaks on qemu-linux-user (docker/qemu-arm)

Platform: Windows 11 22000.469
When attempted to run psp2cldr on Docker / qemu-arm, a noticeable memory leak accumulates very quickly when threads are created.

Narrowed down the issue further to the following sample program,

// compile with `gcc a.c -pthread`

#include <assert.h>
#include <pthread.h>

#define MAGIC_RETURN ((void *)42)

void *thread_main(void *arg)
{
    return MAGIC_RETURN;
}

int main(int argc, char *argv[])
{
    size_t i;
    for (i = 0;; i++)
    {
        pthread_t thread;
        assert(pthread_create(&thread, NULL, thread_main, NULL) == 0);
        void *ret;
        assert(pthread_join(thread, &ret) == 0);
        assert(ret == MAGIC_RETURN);
    }

    return 0;
}

A pmap -XX is attached (a.log) for the above sample on arm32v7/fedora:35.
Notice that the total mapped memory in vaddr space is 16066068 KiB.

Reproducible with multiple distributions from docker hub, including arm32v7/alpine:3.15.0 (musl), arm32v7/ubuntu:focal-20220113 (glibc), and arm32v7/fedora:35 (glibc).
Also reproducible on qemu-arm version 6.1.0 (qemu-6.1.0-10.fc35) with gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf 10.2-2020.11 from ARM's GNU Toolchain for the A-profile Architecture.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.