Skip to content

HTTP/2 preface skip shifts pointer without shrinking length #2040

@MrAlias

Description

@MrAlias

Summary

When OBI detects the HTTP/2 client preface, it advances args->u_buf past the preface but leaves args->bytes_len unchanged. Later HTTP/2 frame parsing still uses the old length while reading from the shifted pointer.

Impact

This can over-read past the valid post-preface payload and potentially export adjacent process memory into telemetry. The issue is limited to observability data exposure, so low severity is appropriate.

Environment

  • First identified in: a0a038c
  • Reviewed against current main commit: d51a98d

Evidence

  • bpf/generictracer/protocol_handler.c still shifts args->u_buf by MIN_HTTP2_SIZE.
  • bpf/generictracer/protocol_http2.h still reads frame headers from the shifted pointer.
  • The same parser still uses the unchanged bytes_len as its bound.

Steps to reproduce

  1. From the repository root on main, create /tmp/http2_preface_oob.c with these exact contents:
    #include <arpa/inet.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    enum { MIN_HTTP2_SIZE = 24 };
    
    typedef struct __attribute__((packed)) {
        uint32_t length;
        uint8_t type;
        uint8_t flags;
        uint32_t stream_id;
    } frame_header_t;
    
    typedef struct {
        unsigned char *u_buf;
        int bytes_len;
    } call_protocol_args_t;
    
    typedef struct {
        call_protocol_args_t args;
        uint32_t pos;
    } grpc_frames_ctx_t;
    
    static frame_header_t next_frame(const grpc_frames_ctx_t *g_ctx) {
        const void *offset = (const unsigned char *)g_ctx->args.u_buf + g_ctx->pos;
        frame_header_t header;
    
        memcpy(&header, offset, sizeof(header));
        header.length = ntohl(header.length << 8);
        header.stream_id = ntohl(header.stream_id << 1);
        return header;
    }
    
    int main(void) {
        unsigned char *buf = malloc(MIN_HTTP2_SIZE + 1);
        grpc_frames_ctx_t ctx = {0};
        frame_header_t frame;
    
        memset(buf, 0, MIN_HTTP2_SIZE + 1);
        ctx.args.u_buf = buf;
        ctx.args.bytes_len = MIN_HTTP2_SIZE + 1;
    
        ctx.args.u_buf = ctx.args.u_buf + MIN_HTTP2_SIZE;
        frame = next_frame(&ctx);
    
        printf("frame type=%u len=%u stream=%u\n", frame.type, frame.length, frame.stream_id);
        free(buf);
        return 0;
    }
  2. Build the ASan harness with gcc -fsanitize=address -fno-omit-frame-pointer -O0 -g -o /tmp/http2_preface_oob /tmp/http2_preface_oob.c.
  3. Run /tmp/http2_preface_oob.
  4. Observe that AddressSanitizer reports a heap-buffer-overflow after the HTTP/2 preface skip, proving the parser advances the pointer without shrinking the length.

Suggested Fix Direction

Reduce args->bytes_len whenever the preface is skipped, or track a separate effective length for the post-preface buffer.

Acceptance Criteria

  • Pointer and effective length stay consistent after HTTP/2 preface handling.
  • Small post-preface buffers do not cause parser over-reads.
  • Tests cover preface-only and preface-plus-short-payload cases.

Note

I have reviewed this issue before posting it. It was identified by OpenAI Codex, and the draft was prepared with its assistance, but it may still contain mistakes, missing context, or incorrect conclusions. Please independently validate the behavior, impact, and proposed fix before acting on it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: ebpfKernel-side eBPF program logic and protocol parsingbugSomething isn't workingebpfIssues or PRs that primarily require eBPF program changes

    Type

    No type

    Projects

    Status

    Todo

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions