CTSRD-CHERI / cheribsd

FreeBSD adapted for CHERI-RISC-V and Arm Morello.
http://cheribsd.org
Other
170 stars 60 forks source link

sysctl doesn't export a valid `ps_strings` capability #2223

Closed RoundofThree closed 2 months ago

RoundofThree commented 2 months ago

While working on ASan, I found that sysctl kern.ps_strings doesn't return a valid capability. Here is a simple poc (compiled with cc in CheriBSD Morello releng/24.05 without additional flags):

#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/exec.h>
#include <sys/errno.h>
#include <sys/limits.h>
// #include <sys/auxv.h>

#include <elf.h>
#include <cheri.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// #1: via sysctl kern.ps_strings (stripped tag)
// this is the way used in ASan
int
method1(void)
{
    struct ps_strings *pss;
    size_t sz = sizeof(pss);
    if (sysctlbyname("kern.ps_strings", &pss, &sz, NULL, 0) == -1) {
        printf("sysctl kern.ps_strings failed\n");
        return 1;
    }
    printf("tag: %d\n", __builtin_cheri_tag_get(pss)); // 0
    printf("pss: %#p", (void*)pss);
    fflush(stdout);
    if (!__builtin_cheri_tag_get(pss)) {
        return 1;
    }
    void *argv = pss->ps_argvstr;
    void *envp = pss->ps_envstr;
    printf("argv: %#p\n envp: %#p\n", argv, envp);
    return 0;
}

int
main(void)
{
    int ret;
    ret = method1();
    return (ret);
}

Here is another program that uses sysctl kern.proc.KERN_PROC_AUXV to get AT_PS_STRINGS (the output pss is also untagged):

#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/exec.h>
#include <sys/errno.h>
#include <sys/limits.h>
// #include <sys/auxv.h>

#include <elf.h>
#include <cheri.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// #2: via sysctl kern.proc.KERN_PROC_AUXV, get AT_PS_STRINGS --> stripped
#define PROC_AUXV_MAX   256
int
method2(void)
{
    Elf_Auxinfo *auxv;
    int name[4];
    size_t len;
    pid_t pid;

    pid = getpid();

    name[0] = CTL_KERN;
    name[1] = KERN_PROC;
    name[2] = KERN_PROC_AUXV;
    name[3] = pid;
    len = PROC_AUXV_MAX * sizeof(Elf_Auxinfo);
    auxv = malloc(len);
    if (auxv == NULL) {
        printf("malloc(%zu) failed\n", len);
        return (1);
    }
    if (sysctl(name, nitems(name), auxv, &len, NULL, 0) == -1) {
        if (errno != ESRCH && errno != EPERM)
            printf("error: sysctl: kern.proc.auxv: %d: %d\n", pid, errno);
        free(auxv);
        return (1);
    }
    size_t cnt = len / sizeof(Elf_Auxinfo);
    printf("auxv: %#p, number of aux: %zu\n", auxv, cnt);

    // get PS_STRINGS?
    struct ps_strings *pss;
    for (Elf_Auxinfo *aux = auxv; aux->a_type != AT_NULL; aux++) {
        switch(aux->a_type) {
        case AT_PS_STRINGS:
            pss = aux->a_un.a_ptr;
            break;
        default:
            break;
        }
        if (pss != NULL)
            break;
    }
    printf("tag: %d\n", __builtin_cheri_tag_get(pss)); // 0
    printf("pss: %#p", (void*)pss);
    fflush(stdout);

    return (0);
}

int
main(void)
{
    int ret;
    ret = method2();
    return (ret);
}
jrtc27 commented 2 months ago

Is there a reason not to just use elf_aux_info(AT_PS_STRINGS)? The second sysctl is particularly problematic since it would let you get capabilities for a foreign address space and totally violate your own process's spatial safety. kern.ps_strings (or kern.proc.KERN_PROC_AUXV.pid for the current pid) perhaps less so, but if it's your own process I don't see why you shouldn't just use the in-userspace interface.

brooksdavis commented 2 months ago

sysctl not exporting capabilities[0] is a feature of CheriABI to preserve least privilege and provenance. Things like kern.ps_strings and kern.usrstack would expose powerful capabilities to any code that can make a sysctl, effectively adding that code to the TCB of the application. Software should use elf_aux_info() or (if absolutely necessity) direct access to __elf_aux_vector which at least allows use to be audited and (with c18n) constrained.

[0] There is a SYSCTL_CAPABILITY, but both uses are to expose broad capabilities (for sealing and compartment IDs) to enable experimentation and they should be removed as experiments are completed.

RoundofThree commented 2 months ago

Indeed I think that is a better approach. To provide a bit of context, ASan cannot invoke libc functions, hence I modified it to access __elf_aux_vector. Thanks!