Skip to content
This repository has been archived by the owner on Jan 19, 2023. It is now read-only.

BPF spills register with 1 byte value to use 8 byte on stack #32

Open
williamtu opened this issue Feb 16, 2017 · 2 comments
Open

BPF spills register with 1 byte value to use 8 byte on stack #32

williamtu opened this issue Feb 16, 2017 · 2 comments

Comments

@williamtu
Copy link
Contributor

I still have no idea why such a simple program will spill the 1 byte value to use 8 byte on stack.

#include "xdp_model.p4"
header Ethernet {
    bit<48> source;
}
struct Headers {
    Ethernet ethernet;
}
parser Parser(packet_in packet, out Headers hd) {
    state start {
        packet.extract(hd.ethernet);
        transition accept;
    }
}
control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) {
    apply {
        xout.output_port = 0;
        xout.output_action = xdp_action.XDP_PASS;
    }
}
control Deparser(in Headers hdrs, packet_out packet) {
    apply {
        packet.emit(hdrs.ethernet);
    }
}
xdp(Parser(), Ingress(), Deparser()) main;

then I greatly reduce a lot of stuff, to this very simple C code.

#include "xdp1.h"
#define KBUILD_MODNAME "xdptest"
#include <linux/bpf.h>
#include "bpf_helpers.h"

#define load_byte(data, b)  (*((u8*)(data) + (b)))
#define htonl
#define htons

#define EBPF_MASK(t, w) ((((t)(1)) << (w)) - (t)1)
#define write_byte(base, offset, v)  *(u8*)(((u8*)base) + (offset)) = v 

struct bpf_map_def SEC("maps") ebpf_outTable = {
    .type = BPF_MAP_TYPE_PERCPU_ARRAY,
    .key_size = sizeof(u32),
    .value_size = sizeof(u32),
    .pinning = 2, /* PIN_GLOBAL_NS */
    .max_entries = 1 /* No multicast support */
};

SEC("prog")
int ebpf_filter(struct xdp_md* skb){
    u8 ebpf_byte;
    u32 ebpf_zero = 0;
    u32 ebpf_outHeaderLength = 0;
    struct xdp_output xout;
    /* TODO: this should be initialized by the environment. HOW? */
    struct xdp_input xin;
    struct Headers hd = {};

    void* pktstart = ((u8*)(long)skb->data);
    void* ebpf_packetEnd = ((u8*)(long)skb->data_end);
    
        /* extract(hd.ethernet)*/
        if (ebpf_packetEnd < pktstart + 14) {
            return XDP_ABORTED;
        }
            xout.output_port = 0;
            xout.output_action = XDP_PASS;
        hd.ethernet.source[0] = ((load_byte(pktstart, 0) ));
        hd.ethernet.source[1] = ((load_byte(pktstart, 1) ));
        hd.ethernet.source[2] = ((load_byte(pktstart, 2) ));
        hd.ethernet.source[3] = ((load_byte(pktstart, 3) ));
        hd.ethernet.source[4] = ((load_byte(pktstart, 4) ));
        hd.ethernet.source[5] = ((load_byte(pktstart, 5) ));

        hd.ethernet.ebpf_valid = 1;

    /* deparser */
        if (hd.ethernet.ebpf_valid) ebpf_outHeaderLength += 48;
        
        bpf_xdp_adjust_head(skb, 14 - ebpf_outHeaderLength);
        pktstart = ((u8*)(long)skb->data);
        ebpf_packetEnd = ((u8*)(long)skb->data_end);
        
            /* packet.emit(hd.ethernet)*/
            if (hd.ethernet.ebpf_valid) {
                if (ebpf_packetEnd < pktstart + 14) {
                    return XDP_ABORTED;
                }
                ebpf_byte = hd.ethernet.source[0];
                write_byte(pktstart, 0, (ebpf_byte));
                ebpf_byte = hd.ethernet.source[1];
                write_byte(pktstart, 1, (ebpf_byte));
                ebpf_byte = hd.ethernet.source[2];
                write_byte(pktstart, 2, (ebpf_byte));
                ebpf_byte = hd.ethernet.source[3];
                write_byte(pktstart, 3, (ebpf_byte));
                ebpf_byte = hd.ethernet.source[4];
                write_byte(pktstart, 4, (ebpf_byte));
                ebpf_byte = hd.ethernet.source[5];
                write_byte(pktstart, 5, (ebpf_byte));
            }

    bpf_map_update_elem(&ebpf_outTable, &ebpf_zero, &xout.output_port, BPF_ANY);
    return xout.output_action;
}
char _license[] SEC("license") = "GPL";

Still the objdump shows usage of 8byte on stack

Disassembly of section prog:
ebpf_filter:
; int ebpf_filter(struct xdp_md* skb){
       0:	r7 = r1
       1:	r6 = 0
; u32 ebpf_zero = 0;
       2:	*(u32 *)(r10 - 4) = r6
; void* ebpf_packetEnd = ((u8*)(long)skb->data_end);
       3:	r2 = *(u32 *)(r7 + 4)
; void* pktstart = ((u8*)(long)skb->data);
       4:	r1 = *(u32 *)(r7 + 0)
; if (ebpf_packetEnd < pktstart + 14) {
       5:	r3 = r1
       6:	r3 += 14
       7:	if r3 > r2 goto 40
       8:	r2 = 2
; xout.output_action = XDP_PASS;
       9:	*(u64 *)(r10 - 16) = r2
; hd.ethernet.source[5] = ((load_byte(pktstart, 5) ));
      10:	r2 = *(u8 *)(r1 + 5)
; hd.ethernet.source[4] = ((load_byte(pktstart, 4) ));
      11:	*(u64 *)(r10 - 24) = r2
      12:	r2 = *(u8 *)(r1 + 4)
; hd.ethernet.source[3] = ((load_byte(pktstart, 3) ));
      13:	*(u64 *)(r10 - 32) = r2
      14:	r2 = *(u8 *)(r1 + 3)
; hd.ethernet.source[2] = ((load_byte(pktstart, 2) ));
      15:	*(u64 *)(r10 - 40) = r2
      16:	r2 = *(u8 *)(r1 + 2)
; hd.ethernet.source[1] = ((load_byte(pktstart, 1) ));
      17:	*(u64 *)(r10 - 48) = r2
      18:	r8 = *(u8 *)(r1 + 1)

what I'm hoping to see is s.t like this, with r10 - off, off is diff by 1

; *(u8 *)data = (u8) hds.eth.fields[i]; 
      10:	r7 = *(u8 *)(r6 + 0)
; hds.eth.fields[i] = (u8)(load_byte(data,  BYTES(i*8)) >> 0);
      11:	*(u8 *)(r10 - 14) = r7
      12:	r2 = *(u8 *)(r6 + 1)
      13:	*(u8 *)(r10 - 13) = r2
      14:	r2 = *(u8 *)(r6 + 2)
      15:	*(u8 *)(r10 - 12) = r2
      16:	r2 = *(u8 *)(r6 + 3)
      17:	*(u8 *)(r10 - 11) = r2
      18:	r2 = *(u8 *)(r6 + 4)
      19:	*(u8 *)(r10 - 10) = r2
      20:	r2 = *(u8 *)(r6 + 5)
      21:	*(u8 *)(r10 - 9) = r2
@mihaibudiu
Copy link
Contributor

Unfortunately I don't think there's anything we can do.
You should bring this up at the iovisor summit.

@mihaibudiu
Copy link
Contributor

This issue and #22 are really the same thing.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants