selftests/bpf: Fix test for 4-byte load from dst_port on big-endian
authorJakub Sitnicki <jakub@cloudflare.com>
Thu, 17 Mar 2022 11:39:20 +0000 (12:39 +0100)
committerDaniel Borkmann <daniel@iogearbox.net>
Fri, 18 Mar 2022 14:46:59 +0000 (15:46 +0100)
commitdeb59400464468352b4d7827420e04f1add94726
tree0c5f7fffe2b308ba022fd0263a5cb412a88c0e09
parente06b5bbcf3f107fb5e148714efe2decfb3b4e0ac
selftests/bpf: Fix test for 4-byte load from dst_port on big-endian

The check for 4-byte load from dst_port offset into bpf_sock is failing on
big-endian architecture - s390. The bpf access converter rewrites the
4-byte load to a 2-byte load from sock_common at skc_dport offset, as shown
below.

  * s390 / llvm-objdump -S --no-show-raw-insn

  00000000000002a0 <sk_dst_port__load_word>:
        84:       r1 = *(u32 *)(r1 + 48)
        85:       w0 = 1
        86:       if w1 == 51966 goto +1 <LBB5_2>
        87:       w0 = 0
  00000000000002c0 <LBB5_2>:
        88:       exit

  * s390 / bpftool prog dump xlated

  _Bool sk_dst_port__load_word(struct bpf_sock * sk):
    35: (69) r1 = *(u16 *)(r1 +12)
    36: (bc) w1 = w1
    37: (b4) w0 = 1
    38: (16) if w1 == 0xcafe goto pc+1
    39: (b4) w0 = 0
    40: (95) exit

  * x86_64 / llvm-objdump -S --no-show-raw-insn

  00000000000002a0 <sk_dst_port__load_word>:
        84:       r1 = *(u32 *)(r1 + 48)
        85:       w0 = 1
        86:       if w1 == 65226 goto +1 <LBB5_2>
        87:       w0 = 0
  00000000000002c0 <LBB5_2>:
        88:       exit

  * x86_64 / bpftool prog dump xlated

  _Bool sk_dst_port__load_word(struct bpf_sock * sk):
    33: (69) r1 = *(u16 *)(r1 +12)
    34: (b4) w0 = 1
    35: (16) if w1 == 0xfeca goto pc+1
    36: (b4) w0 = 0
    37: (95) exit

This leads to surprises if we treat the destination register contents as a
32-bit value, ignoring the fact that in reality it contains a 16-bit value.

On little-endian the register contents reflect the bpf_sock struct
definition, where the lower 16-bits contain the port number:

struct bpf_sock {
...
__be16 dst_port; /* offset 48 */
__u16 :16;
...
};

However, on big-endian the register contents suggest that field the layout
of bpf_sock struct is as so:

struct bpf_sock {
...
__u16 :16; /* offset 48 */
__be16 dst_port;
...
};

Account for this quirky access conversion in the test case exercising the
4-byte load by treating the result as 16-bit wide.

Signed-off-by: Jakub Sitnicki <jakub@cloudflare.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/bpf/20220317113920.1068535-5-jakub@cloudflare.com
tools/testing/selftests/bpf/progs/test_sock_fields.c