Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
sudo: required
language: bash
dist: bionic
services:
- docker

env:
global:
- PROJECT_NAME='libbpf'
- AUTHOR_EMAIL="$(git log -1 --pretty=\"%aE\")"
- REPO_ROOT="$TRAVIS_BUILD_DIR"
- CI_ROOT="$REPO_ROOT/travis-ci"
- VMTEST_ROOT="$CI_ROOT/vmtest"

addons:
apt:
packages:
- qemu-kvm
- zstd
- binutils-dev
- elfutils
- libcap-dev
- libelf-dev
- libdw-dev
- python3-docutils

jobs:
include:
- stage: Builds & Tests
name: Kernel LATEST + selftests
language: bash
env: KERNEL=LATEST
script: $CI_ROOT/vmtest/run_vmtest.sh || travis_terminate 1
86 changes: 62 additions & 24 deletions arch/x86/net/bpf_jit_comp.c
Original file line number Diff line number Diff line change
Expand Up @@ -789,8 +789,31 @@ static void detect_reg_usage(struct bpf_insn *insn, int insn_cnt,
}
}

static int emit_nops(u8 **pprog, int len)
{
u8 *prog = *pprog;
int i, noplen, cnt = 0;

while (len > 0) {
noplen = len;

if (noplen > ASM_NOP_MAX)
noplen = ASM_NOP_MAX;

for (i = 0; i < noplen; i++)
EMIT1(ideal_nops[noplen][i]);
len -= noplen;
}

*pprog = prog;

return cnt;
}

#define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp)))

static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
int oldproglen, struct jit_context *ctx)
int oldproglen, struct jit_context *ctx, bool jmp_padding)
{
bool tail_call_reachable = bpf_prog->aux->tail_call_reachable;
struct bpf_insn *insn = bpf_prog->insnsi;
Expand Down Expand Up @@ -824,6 +847,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
u8 jmp_cond;
int ilen;
u8 *func;
int nops;

switch (insn->code) {
/* ALU */
Expand Down Expand Up @@ -1409,6 +1433,13 @@ xadd: if (is_imm8(insn->off))
}
jmp_offset = addrs[i + insn->off] - addrs[i];
if (is_imm8(jmp_offset)) {
if (jmp_padding) {
nops = INSN_SZ_DIFF - 2;
WARN_ONCE((nops != 0 && nops != 4),
"unexpected cond_jmp padding: %d bytes\n",
nops);
cnt += emit_nops(&prog, nops);
}
EMIT2(jmp_cond, jmp_offset);
} else if (is_simm32(jmp_offset)) {
EMIT2_off32(0x0F, jmp_cond + 0x10, jmp_offset);
Expand All @@ -1431,11 +1462,29 @@ xadd: if (is_imm8(insn->off))
else
jmp_offset = addrs[i + insn->off] - addrs[i];

if (!jmp_offset)
/* Optimize out nop jumps */
if (!jmp_offset) {
/*
* If jmp_padding is enabled, the extra nops will
* be inserted. Otherwise, optimize out nop jumps.
*/
if (jmp_padding) {
nops = INSN_SZ_DIFF;
WARN_ONCE((nops != 0 && nops != 2 && nops != 5),
"unexpected nop jump padding: %d bytes\n",
nops);
cnt += emit_nops(&prog, nops);
}
break;
}
emit_jmp:
if (is_imm8(jmp_offset)) {
if (jmp_padding) {
nops = INSN_SZ_DIFF - 2;
WARN_ONCE((nops != 0 && nops != 3),
"unexpected jump padding: %d bytes\n",
nops);
cnt += emit_nops(&prog, INSN_SZ_DIFF - 2);
}
EMIT2(0xEB, jmp_offset);
} else if (is_simm32(jmp_offset)) {
EMIT1_off32(0xE9, jmp_offset);
Expand Down Expand Up @@ -1578,26 +1627,6 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
return 0;
}

static void emit_nops(u8 **pprog, unsigned int len)
{
unsigned int i, noplen;
u8 *prog = *pprog;
int cnt = 0;

while (len > 0) {
noplen = len;

if (noplen > ASM_NOP_MAX)
noplen = ASM_NOP_MAX;

for (i = 0; i < noplen; i++)
EMIT1(ideal_nops[noplen][i]);
len -= noplen;
}

*pprog = prog;
}

static void emit_align(u8 **pprog, u32 align)
{
u8 *target, *prog = *pprog;
Expand Down Expand Up @@ -1970,8 +1999,12 @@ struct x64_jit_data {
u8 *image;
int proglen;
struct jit_context ctx;
bool padded;
};

#define MAX_PASSES 20
#define PADDING_PASSES (MAX_PASSES - 5)

struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
{
struct bpf_binary_header *header = NULL;
Expand All @@ -1981,6 +2014,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
struct jit_context ctx = {};
bool tmp_blinded = false;
bool extra_pass = false;
bool padding = false;
u8 *image = NULL;
int *addrs;
int pass;
Expand Down Expand Up @@ -2010,6 +2044,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
}
prog->aux->jit_data = jit_data;
}
padding = jit_data->padded;
addrs = jit_data->addrs;
if (addrs) {
ctx = jit_data->ctx;
Expand Down Expand Up @@ -2043,7 +2078,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
* pass to emit the final image.
*/
for (pass = 0; pass < 20 || image; pass++) {
proglen = do_jit(prog, addrs, image, oldproglen, &ctx);
if (!padding && pass >= PADDING_PASSES)
padding = true;
proglen = do_jit(prog, addrs, image, oldproglen, &ctx, padding);
if (proglen <= 0) {
out_image:
image = NULL;
Expand Down Expand Up @@ -2097,6 +2134,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
jit_data->proglen = proglen;
jit_data->image = image;
jit_data->header = header;
jit_data->padded = padding;
}
prog->bpf_func = (void *)image;
prog->jited = 1;
Expand Down
7 changes: 1 addition & 6 deletions lib/test_bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ static int __bpf_fill_ja(struct bpf_test *self, unsigned int len,

static int bpf_fill_maxinsns11(struct bpf_test *self)
{
/* Hits 70 passes on x86_64, so cannot get JITed there. */
/* Hits 70 passes on x86_64 and triggers NOPs padding. */
return __bpf_fill_ja(self, BPF_MAXINSNS, 68);
}

Expand Down Expand Up @@ -5318,15 +5318,10 @@ static struct bpf_test tests[] = {
{
"BPF_MAXINSNS: Jump, gap, jump, ...",
{ },
#if defined(CONFIG_BPF_JIT_ALWAYS_ON) && defined(CONFIG_X86)
CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
#else
CLASSIC | FLAG_NO_DATA,
#endif
{ },
{ { 0, 0xababcbac } },
.fill_helper = bpf_fill_maxinsns11,
.expected_errcode = -ENOTSUPP,
},
{
"BPF_MAXINSNS: jump over MSH",
Expand Down
43 changes: 43 additions & 0 deletions tools/testing/selftests/bpf/test_verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,49 @@ static void bpf_fill_scale(struct bpf_test *self)
}
}

static int bpf_fill_torturous_jumps_insn(struct bpf_insn *insn)
{
unsigned int len = 259, hlen = 128;
int i;

insn[0] = BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32);
for (i = 1; i <= hlen; i++) {
insn[i] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, i, hlen);
insn[i + hlen] = BPF_JMP_A(hlen - i);
}
insn[len - 2] = BPF_MOV64_IMM(BPF_REG_0, 1);
insn[len - 1] = BPF_EXIT_INSN();

return len;
}

static void bpf_fill_torturous_jumps(struct bpf_test *self)
{
struct bpf_insn *insn = self->fill_insns;
int i = 0;

switch (self->retval) {
case 1:
self->prog_len = bpf_fill_torturous_jumps_insn(insn);
return;
case 2:
/* main */
insn[i++] = BPF_RAW_INSN(BPF_JMP|BPF_CALL, 0, 1, 0, 3);
insn[i++] = BPF_ST_MEM(BPF_B, BPF_REG_10, -32, 0);
insn[i++] = BPF_MOV64_IMM(BPF_REG_0, 2);
insn[i++] = BPF_EXIT_INSN();

/* subprog */
i += bpf_fill_torturous_jumps_insn(insn + i);

self->prog_len = i;
return;
default:
self->prog_len = 0;
break;
}
}

/* BPF_SK_LOOKUP contains 13 instructions, if you need to fix up maps */
#define BPF_SK_LOOKUP(func) \
/* struct bpf_sock_tuple tuple = {} */ \
Expand Down
16 changes: 16 additions & 0 deletions tools/testing/selftests/bpf/verifier/jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,19 @@
.result = ACCEPT,
.retval = 2,
},
{
"jit: torturous jumps",
.insns = { },
.fill_helper = bpf_fill_torturous_jumps,
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = ACCEPT,
.retval = 1,
},
{
"jit: torturous jumps in subprog",
.insns = { },
.fill_helper = bpf_fill_torturous_jumps,
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
.result = ACCEPT,
.retval = 2,
},
84 changes: 84 additions & 0 deletions travis-ci/managers/debian.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/bin/bash

PHASES=(${@:-SETUP RUN RUN_ASAN CLEANUP})
DEBIAN_RELEASE="${DEBIAN_RELEASE:-testing}"
CONT_NAME="${CONT_NAME:-libbpf-debian-$DEBIAN_RELEASE}"
ENV_VARS="${ENV_VARS:-}"
DOCKER_RUN="${DOCKER_RUN:-docker run}"
REPO_ROOT="${REPO_ROOT:-$PWD}"
ADDITIONAL_DEPS=(clang pkg-config gcc-8)
CFLAGS="-g -O2 -Werror -Wall"

function info() {
echo -e "\033[33;1m$1\033[0m"
}

function error() {
echo -e "\033[31;1m$1\033[0m"
}

function docker_exec() {
docker exec $ENV_VARS -it $CONT_NAME "$@"
}

set -e

source "$(dirname $0)/travis_wait.bash"

for phase in "${PHASES[@]}"; do
case $phase in
SETUP)
info "Setup phase"
info "Using Debian $DEBIAN_RELEASE"

sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
docker --version

docker pull debian:$DEBIAN_RELEASE
info "Starting container $CONT_NAME"
$DOCKER_RUN -v $REPO_ROOT:/build:rw \
-w /build --privileged=true --name $CONT_NAME \
-dit --net=host debian:$DEBIAN_RELEASE /bin/bash
docker_exec bash -c "echo deb-src http://deb.debian.org/debian $DEBIAN_RELEASE main >>/etc/apt/sources.list"
docker_exec apt-get -y update
docker_exec apt-get -y build-dep libelf-dev
docker_exec apt-get -y install libelf-dev
docker_exec apt-get -y install "${ADDITIONAL_DEPS[@]}"
;;
RUN|RUN_CLANG|RUN_GCC8|RUN_ASAN|RUN_CLANG_ASAN|RUN_GCC8_ASAN)
if [[ "$phase" = *"CLANG"* ]]; then
ENV_VARS="-e CC=clang -e CXX=clang++"
CC="clang"
elif [[ "$phase" = *"GCC8"* ]]; then
ENV_VARS="-e CC=gcc-8 -e CXX=g++-8"
CC="gcc-8"
else
CFLAGS="${CFLAGS} -Wno-stringop-truncation"
fi
if [[ "$phase" = *"ASAN"* ]]; then
CFLAGS="${CFLAGS} -fsanitize=address,undefined"
fi
docker_exec mkdir build install
docker_exec ${CC:-cc} --version
info "build"
docker_exec make -j$((4*$(nproc))) CFLAGS="${CFLAGS}" -C ./src -B OBJDIR=../build
info "ldd build/libbpf.so:"
docker_exec ldd build/libbpf.so
if ! docker_exec ldd build/libbpf.so | grep -q libelf; then
error "No reference to libelf.so in libbpf.so!"
exit 1
fi
info "install"
docker_exec make -j$((4*$(nproc))) -C src OBJDIR=../build DESTDIR=../install install
docker_exec rm -rf build install
;;
CLEANUP)
info "Cleanup phase"
docker stop $CONT_NAME
docker rm -f $CONT_NAME
;;
*)
echo >&2 "Unknown phase '$phase'"
exit 1
esac
done
Loading