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
93 changes: 93 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: bpf-ci

on:
pull_request:

concurrency:
group: ci-test-${{ github.head_ref }}
cancel-in-progress: true

jobs:
llvm-toolchain:
runs-on: ubuntu-latest
outputs:
llvm: ${{ steps.llvm-toolchain-impl.outputs.version }}
steps:
- id: llvm-version
uses: libbpf/ci/get-llvm-version@master
- id: llvm-toolchain-impl
shell: bash
run: echo "::set-output name=version::llvm-${{ steps.llvm-version.outputs.version }}"
set-matrix:
needs: llvm-toolchain
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: |
echo "::set-output name=matrix::{\"include\":[ \
{\"kernel\": \"LATEST\", \"runs_on\": [\"ubuntu-latest\", \"self-hosted\"], \"arch\": \"x86_64\", \"toolchain\": \"gcc\"}, \
{\"kernel\": \"LATEST\", \"runs_on\": [\"ubuntu-latest\", \"self-hosted\"], \"arch\": \"x86_64\", \"toolchain\": \"${{ needs.llvm-toolchain.outputs.llvm }}\"}, \
{\"kernel\": \"LATEST\", \"runs_on\": [\"z15\", \"self-hosted\"], \"arch\": \"s390x\", \"toolchain\": \"gcc\"} \
]}"
VM_Test:
needs: set-matrix
runs-on: ${{ matrix.runs_on }}
name: Kernel ${{ matrix.kernel }} on ${{ matrix.runs_on[0] }} with ${{ matrix.toolchain }}
timeout-minutes: 100
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.set-matrix.outputs.matrix) }}
env:
KERNEL: ${{ matrix.kernel }}
REPO_ROOT: ${{ github.workspace }}
REPO_PATH: ""
steps:
- uses: actions/checkout@v2
- if: ${{ github.repository == 'kernel-patches/vmtest' }}
name: Download bpf-next tree
uses: libbpf/ci/get-linux-source@master
with:
dest: '.kernel'
- if: ${{ github.repository == 'kernel-patches/vmtest' }}
name: Move linux source in place
shell: bash
run: |
rm -rf .kernel/.git
cp -rf .kernel/. .
rm -rf .kernel
- uses: libbpf/ci/patch-kernel@master
with:
patches-root: '${{ github.workspace }}/travis-ci/diffs'
repo-root: '${{ github.workspace }}'
- name: Setup build environment
uses: libbpf/ci/setup-build-env@master
- name: Build kernel image
uses: libbpf/ci/build-linux@master
with:
arch: ${{ matrix.arch }}
toolchain: ${{ matrix.toolchain }}
- name: Build selftests
uses: libbpf/ci/build-selftests@master
with:
vmlinux_btf: ${{ github.workspace }}/vmlinux
toolchain: ${{ matrix.toolchain }}
- name: Build samples
uses: libbpf/ci/build-samples@master
with:
vmlinux_btf: ${{ github.workspace }}/vmlinux
toolchain: ${{ matrix.toolchain }}
- name: Prepare rootfs
uses: libbpf/ci/prepare-rootfs@master
with:
project-name: 'libbpf'
arch: ${{ matrix.arch }}
kernel-root: '.'
image-output: '/tmp/root.img'
- name: Run selftests
uses: libbpf/ci/run-qemu@master
with:
arch: ${{ matrix.arch}}
img: '/tmp/root.img'
vmlinuz: '${{ github.workspace }}/vmlinuz'
18 changes: 0 additions & 18 deletions README
Original file line number Diff line number Diff line change
@@ -1,18 +0,0 @@
Linux kernel
============

There are several guides for kernel developers and users. These guides can
be rendered in a number of formats, like HTML and PDF. Please read
Documentation/admin-guide/README.rst first.

In order to build the documentation, use ``make htmldocs`` or
``make pdfdocs``. The formatted documentation can also be read online at:

https://www.kernel.org/doc/html/latest/

There are various text files in the Documentation/ subdirectory,
several of them using the Restructured Text markup notation.

Please read the Documentation/process/changes.rst file, as it contains the
requirements for building and running the kernel, and information about
the problems which may result by upgrading your kernel.
11 changes: 11 additions & 0 deletions include/linux/bpf_verifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,17 @@ struct bpf_reference_state {
* is used purely to inform the user of a reference leak.
*/
int insn_idx;
/* There can be a case like:
* main (frame 0)
* cb (frame 1)
* func (frame 3)
* cb (frame 4)
* Hence for frame 4, if callback_ref just stored boolean, it would be
* impossible to distinguish nested callback refs. Hence store the
* frameno and compare that to callback_ref in check_reference_leak when
* exiting a callback function.
*/
int callback_ref;
};

/* state of the program:
Expand Down
8 changes: 4 additions & 4 deletions kernel/bpf/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1633,10 +1633,6 @@ bpf_base_func_proto(enum bpf_func_id func_id)
return &bpf_ringbuf_submit_dynptr_proto;
case BPF_FUNC_ringbuf_discard_dynptr:
return &bpf_ringbuf_discard_dynptr_proto;
case BPF_FUNC_for_each_map_elem:
return &bpf_for_each_map_elem_proto;
case BPF_FUNC_loop:
return &bpf_loop_proto;
case BPF_FUNC_strncmp:
return &bpf_strncmp_proto;
case BPF_FUNC_dynptr_from_mem:
Expand Down Expand Up @@ -1675,6 +1671,10 @@ bpf_base_func_proto(enum bpf_func_id func_id)
return &bpf_timer_cancel_proto;
case BPF_FUNC_kptr_xchg:
return &bpf_kptr_xchg_proto;
case BPF_FUNC_for_each_map_elem:
return &bpf_for_each_map_elem_proto;
case BPF_FUNC_loop:
return &bpf_loop_proto;
default:
break;
}
Expand Down
42 changes: 33 additions & 9 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,7 @@ static int acquire_reference_state(struct bpf_verifier_env *env, int insn_idx)
id = ++env->id_gen;
state->refs[new_ofs].id = id;
state->refs[new_ofs].insn_idx = insn_idx;
state->refs[new_ofs].callback_ref = state->in_callback_fn ? state->frameno : 0;

return id;
}
Expand All @@ -1098,6 +1099,9 @@ static int release_reference_state(struct bpf_func_state *state, int ptr_id)
last_idx = state->acquired_refs - 1;
for (i = 0; i < state->acquired_refs; i++) {
if (state->refs[i].id == ptr_id) {
/* Cannot release caller references in callbacks */
if (state->in_callback_fn && state->refs[i].callback_ref != state->frameno)
return -EINVAL;
if (last_idx && i != last_idx)
memcpy(&state->refs[i], &state->refs[last_idx],
sizeof(*state->refs));
Expand Down Expand Up @@ -6938,10 +6942,17 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
caller->regs[BPF_REG_0] = *r0;
}

/* Transfer references to the caller */
err = copy_reference_state(caller, callee);
if (err)
return err;
/* callback_fn frame should have released its own additions to parent's
* reference state at this point, or check_reference_leak would
* complain, hence it must be the same as the caller. There is no need
* to copy it back.
*/
if (!callee->in_callback_fn) {
/* Transfer references to the caller */
err = copy_reference_state(caller, callee);
if (err)
return err;
}

*insn_idx = callee->callsite + 1;
if (env->log.level & BPF_LOG_LEVEL) {
Expand Down Expand Up @@ -7065,13 +7076,20 @@ record_func_key(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
static int check_reference_leak(struct bpf_verifier_env *env)
{
struct bpf_func_state *state = cur_func(env);
bool refs_lingering = false;
int i;

if (state->frameno && !state->in_callback_fn)
return 0;

for (i = 0; i < state->acquired_refs; i++) {
if (state->in_callback_fn && state->refs[i].callback_ref != state->frameno)
continue;
verbose(env, "Unreleased reference id=%d alloc_insn=%d\n",
state->refs[i].id, state->refs[i].insn_idx);
refs_lingering = true;
}
return state->acquired_refs ? -EINVAL : 0;
return refs_lingering ? -EINVAL : 0;
}

static int check_bpf_snprintf_call(struct bpf_verifier_env *env,
Expand Down Expand Up @@ -12332,6 +12350,16 @@ static int do_check(struct bpf_verifier_env *env)
return -EINVAL;
}

/* We must do check_reference_leak here before
* prepare_func_exit to handle the case when
* state->curframe > 0, it may be a callback
* function, for which reference_state must
* match caller reference state when it exits.
*/
err = check_reference_leak(env);
if (err)
return err;

if (state->curframe) {
/* exit from nested function */
err = prepare_func_exit(env, &env->insn_idx);
Expand All @@ -12341,10 +12369,6 @@ static int do_check(struct bpf_verifier_env *env)
continue;
}

err = check_reference_leak(env);
if (err)
return err;

err = check_return_code(env);
if (err)
return err;
Expand Down
48 changes: 48 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/cb_refs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0
#include "bpf/libbpf.h"
#include <test_progs.h>
#include <network_helpers.h>

#include "cb_refs.skel.h"

static char log_buf[1024 * 1024];

struct {
const char *prog_name;
const char *err_msg;
} cb_refs_tests[] = {
{ "underflow_prog", "reference has not been acquired before" },
{ "leak_prog", "Unreleased reference" },
{ "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */
{ "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */
};

void test_cb_refs(void)
{
LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_buf = log_buf,
.kernel_log_size = sizeof(log_buf),
.kernel_log_level = 1);
struct bpf_program *prog;
struct cb_refs *skel;
int i;

for (i = 0; i < ARRAY_SIZE(cb_refs_tests); i++) {
LIBBPF_OPTS(bpf_test_run_opts, run_opts,
.data_in = &pkt_v4,
.data_size_in = sizeof(pkt_v4),
.repeat = 1,
);
skel = cb_refs__open_opts(&opts);
if (!ASSERT_OK_PTR(skel, "cb_refs__open_and_load"))
return;
prog = bpf_object__find_program_by_name(skel->obj, cb_refs_tests[i].prog_name);
bpf_program__set_autoload(prog, true);
if (!ASSERT_ERR(cb_refs__load(skel), "cb_refs__load"))
bpf_prog_test_run_opts(bpf_program__fd(prog), &run_opts);
if (!ASSERT_OK_PTR(strstr(log_buf, cb_refs_tests[i].err_msg), "expected error message")) {
fprintf(stderr, "Expected: %s\n", cb_refs_tests[i].err_msg);
fprintf(stderr, "Verifier: %s\n", log_buf);
}
cb_refs__destroy(skel);
}
}
Loading