pwndbg/pwndbg

AArch64 MTE support, broken telescope and others commands..

Open

#3,270 opened on Aug 26, 2025

View on GitHub
 (4 comments) (0 reactions) (0 assignees)Python (5,820 stars) (776 forks)batch import
bughelp wanted

Description

Blog for reading: https://www.linaro.org/blog/adding-support-for-mte-debugging-to-qemu/

Telescope: ^ should display good values for tagged addresses

vmmap:

Example program:

//  zig cc -target aarch64-linux-musl -march=neoverse_n2+mte -O0 -g arm64-mte.c -o mte_demo

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/auxv.h>
#include <asm/hwcap.h>
#include <sys/prctl.h>
#include <signal.h>
#include <unistd.h>
#include <stdint.h>
#include <arm_acle.h>

#define PAGE_SIZE 4096

/*
 * Insert a random logical tag into the given pointer.
 * IRG instruction.
 */
#define insert_random_tag(ptr) ({                       \
        uint64_t __val;                                 \
        asm("irg %0, %1" : "=r" (__val) : "r" (ptr));   \
        __val;                                          \
})

/*
 * Set the allocation tag on the destination address.
 * STG instruction.
 */
#define set_tag(tagged_addr) do {                                      \
        asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \
} while (0)

static void segv_handler(int sig, siginfo_t *si, void *unused) {
    printf("Caught SIGSEGV at address %p (MTE violation)\n", si->si_addr);
    _exit(1);
}

#define PR_MTE_TAG_SHIFT	3

int main() {
    unsigned long hwcap2 = getauxval(AT_HWCAP2);
    if (!(hwcap2 & HWCAP2_MTE)) {
        printf("MTE not supported on this CPU\n");
        return 0;
    }
    printf("MTE supported!\n");

    prctl(PR_SET_TAGGED_ADDR_CTRL,
          PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | (0xfffe << PR_MTE_TAG_SHIFT), 0, 0, 0);

    // zainstaluj handler SIGSEGV
    struct sigaction sa = {0};
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = segv_handler;
    sigaction(SIGSEGV, &sa, NULL);

    // alokacja pamięci z PROT_MTE
    unsigned char *ptr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE|PROT_MTE,
                   MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

    /*
     * Print the pointer value with the default tag (expecting 0)
     */
    printf("pointer is %p\n", ptr);

    /*
     * Write the first 2 bytes of the memory with the default tag
     */
    ptr[0] = 0x41;
    ptr[1] = 0x42;

    /*
     * Read back to confirm the writes
     */
    printf("ptr[0] = 0x%hhx ptr[1] = 0x%hhx\n", ptr[0], ptr[1]);

    /*
     * Generate a random tag and store it for the address (IRG instruction)
     */
    ptr = (unsigned char *) insert_random_tag(ptr);

    /*
     * Set the key on the pointer to match the lock on the memory  (STG instruction)
     */
    set_tag(ptr);

    /*
     * Print the pointer value with the new tag
     */
    printf("pointer is now %p\n", ptr);

    /*
     * Write the first 2 bytes of the memory again, with the new tag
     */
    ptr[0] = 0x43;
    ptr[1] = 0x44;

    /*
     * Read back to confirm the writes
     */
    printf("ptr[0] = 0x%hhx ptr[1] = 0x%hhx\n", ptr[0], ptr[1]);
    while(1) {}

    /*
     * Write to memory beyond the 16 byte granule (offsest 0x10)
     * MTE should generate an exception
     * If the offset is less than 0x10 no SIGSEGV will occur.
     */
    printf("Expecting SIGSEGV...\n");
    ptr[0x10] = 0x55;
    return 0;
}

Contributor guide