mbox series

[RFC,v2,0/5] Make BPF ring buffer overwritable

Message ID 20220906195656.33021-1-flaniel@linux.microsoft.com (mailing list archive)
Headers show
Series Make BPF ring buffer overwritable | expand

Message

Francis Laniel Sept. 6, 2022, 7:56 p.m. UTC
Hi.


First, I hope you are fine and the same for your relatives.

Normally, when BPF ring buffer are full, producers cannot write anymore and
need to wait for consumer to get some data.
As a consequence, calling bpf_ringbuf_reserve() from eBPF code returns NULL.

This contribution adds a new flag to make BPF ring buffer overwritable.
Perf ring buffers already implement an option to be overwritable. In order to
avoid data corruption, the data is written backward, see
commit 9ecda41acb97 ("perf/core: Add ::write_backward attribute to perf event").
This patch series re-uses the same idea from perf ring buffers but in BPF ring
buffers.
So, calling bpf_ringbuf_reserve() on an overwritable BPF ring buffer never
returns NULL.
As a consequence, oldest data will be overwritten by the newest so consumer will
loose data.

Overwritable ring buffers are useful in BPF programs that are permanently
enabled but rarely read, only on-demand, for example in case of a user request
to investigate problems. We would like to use this in the Traceloop project [1].

The self test added in this series was tested and validated in a VM:
you@vm# ./share/linux/tools/testing/selftests/bpf/test_progs -t ringbuf_over
Can't find bpf_testmod.ko kernel module: -2
WARNING! Selftests relying on bpf_testmod.ko will be skipped.
#135     ringbuf_over_writable:OK
Summary: 1/0 PASSED, 0 SKIPPED, 0 FAILED

You can also test the libbpf implementation by using the last patch of this
series which should be applied to iovisor/bcc:
you@home$ cd /path/to/iovisor/bcc
you@home$ git am -3 v2-0005-for-test-purpose-only-Add-toy-to-play-with-BPF-ri.patch
you@home$ cd /path/to/linux/tools/lib/bpf
you@home$ make -j$(nproc)
you@home$ cp libbpf.a /path/to/iovisor/bcc/libbpf-tools/.output
you@home$ cd /path/to/iovisor/bcc/libbpf-tools/
you@home$ make -j toy
# Start your VM and copy toy executable inside it.
root@vm-amd64:~# ./share/toy &
[1] 287
root@vm-amd64:~# for i in {1..16}; do ls > /dev/null; done
16
15
14
13
12
11
10
9
root@vm-amd64:~# ls > /dev/null && ls > /dev/null
18
17

As you can see, the first eight events are overwritten.

If you see any way to improve this contribution, feel free to share.

Changes since:
 v1:
  * Made producers write backward like perf ring buffer, so it permits avoiding
  memory corruption.
  * Added libbpf implementation to consume all events available.
  * Added selftest.
  * Added documentation.

Francis Laniel (5):
  bpf: Make ring buffer overwritable.
  selftests: Add BPF overwritable ring buffer self tests.
  docs/bpf: Add documentation for overwritable ring buffer.
  libbpf: Add implementation to consume overwritable BPF ring buffer.
  for test purpose only: Add toy to play with BPF ring.

 ...-only-Add-toy-to-play-with-BPF-ring-.patch | 147 ++++++++++++++++
 Documentation/bpf/ringbuf.rst                 |  18 +-
 include/uapi/linux/bpf.h                      |   3 +
 kernel/bpf/ringbuf.c                          |  43 +++--
 tools/include/uapi/linux/bpf.h                |   3 +
 tools/lib/bpf/ringbuf.c                       | 106 ++++++++++++
 tools/testing/selftests/bpf/Makefile          |   5 +-
 .../bpf/prog_tests/ringbuf_overwritable.c     | 158 ++++++++++++++++++
 .../bpf/progs/test_ringbuf_overwritable.c     |  61 +++++++
 9 files changed, 531 insertions(+), 13 deletions(-)
 create mode 100644 0001-for-test-purpose-only-Add-toy-to-play-with-BPF-ring-.patch
 create mode 100644 tools/testing/selftests/bpf/prog_tests/ringbuf_overwritable.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_ringbuf_overwritable.c


Best regards and thank you in advance.
---
[1] https://github.com/kinvolk/traceloop
Traceloop was presented at LPC 2020 (https://lpc.events/event/7/contributions/667/)
--
2.25.1

Comments

Andrii Nakryiko Sept. 28, 2022, 12:12 a.m. UTC | #1
On Tue, Sep 6, 2022 at 12:58 PM Francis Laniel
<flaniel@linux.microsoft.com> wrote:
>
> Hi.
>
>
> First, I hope you are fine and the same for your relatives.
>
> Normally, when BPF ring buffer are full, producers cannot write anymore and
> need to wait for consumer to get some data.
> As a consequence, calling bpf_ringbuf_reserve() from eBPF code returns NULL.
>
> This contribution adds a new flag to make BPF ring buffer overwritable.
> Perf ring buffers already implement an option to be overwritable. In order to
> avoid data corruption, the data is written backward, see

No, you are not avoiding data corruption. This patch set doesn't apply
cleanly, so I can't try this locally, but try the following example:

1. Allocate very small ringbuf (4KB).
2. Write BPF program that does multiple reservations within single
run. Enough reservations to overfill entire ringbuf and wrap over. For
each reserved sample fill it completely with garbage.
3. Try to submit all (successful) reservations. My theory is you'll
observe a crash.

Make sure that sample size isn't an exact multiple of ringbuf size so
that you don't have a nice neat overlap.

Look at bpf_ringbuf_restore_from_rec(). Each successful reservation
contains a very delicate and precise offset that allows
bpf_ringbuf_commit() to find the BPF ringbuf map in memory. By
allowing to overwrite active not-yet-submitted reservation you allow
to corrupt this information in the record header. This will make
kernel code to dereference garbage addresses.

This whole backwards approach won't work with BPF ringbuf. It works
with BPF perfbuf only under some non-enforceable assumptions, from
what I understand. We need something else for an overwritable ringbuf.


> commit 9ecda41acb97 ("perf/core: Add ::write_backward attribute to perf event").
> This patch series re-uses the same idea from perf ring buffers but in BPF ring
> buffers.
> So, calling bpf_ringbuf_reserve() on an overwritable BPF ring buffer never
> returns NULL.
> As a consequence, oldest data will be overwritten by the newest so consumer will
> loose data.
>
> Overwritable ring buffers are useful in BPF programs that are permanently
> enabled but rarely read, only on-demand, for example in case of a user request
> to investigate problems. We would like to use this in the Traceloop project [1].
>
> The self test added in this series was tested and validated in a VM:
> you@vm# ./share/linux/tools/testing/selftests/bpf/test_progs -t ringbuf_over
> Can't find bpf_testmod.ko kernel module: -2
> WARNING! Selftests relying on bpf_testmod.ko will be skipped.
> #135     ringbuf_over_writable:OK
> Summary: 1/0 PASSED, 0 SKIPPED, 0 FAILED
>
> You can also test the libbpf implementation by using the last patch of this
> series which should be applied to iovisor/bcc:
> you@home$ cd /path/to/iovisor/bcc
> you@home$ git am -3 v2-0005-for-test-purpose-only-Add-toy-to-play-with-BPF-ri.patch
> you@home$ cd /path/to/linux/tools/lib/bpf
> you@home$ make -j$(nproc)
> you@home$ cp libbpf.a /path/to/iovisor/bcc/libbpf-tools/.output
> you@home$ cd /path/to/iovisor/bcc/libbpf-tools/
> you@home$ make -j toy
> # Start your VM and copy toy executable inside it.
> root@vm-amd64:~# ./share/toy &
> [1] 287
> root@vm-amd64:~# for i in {1..16}; do ls > /dev/null; done
> 16
> 15
> 14
> 13
> 12
> 11
> 10
> 9
> root@vm-amd64:~# ls > /dev/null && ls > /dev/null
> 18
> 17
>
> As you can see, the first eight events are overwritten.
>
> If you see any way to improve this contribution, feel free to share.
>
> Changes since:
>  v1:
>   * Made producers write backward like perf ring buffer, so it permits avoiding
>   memory corruption.
>   * Added libbpf implementation to consume all events available.
>   * Added selftest.
>   * Added documentation.
>
> Francis Laniel (5):
>   bpf: Make ring buffer overwritable.
>   selftests: Add BPF overwritable ring buffer self tests.
>   docs/bpf: Add documentation for overwritable ring buffer.
>   libbpf: Add implementation to consume overwritable BPF ring buffer.
>   for test purpose only: Add toy to play with BPF ring.
>
>  ...-only-Add-toy-to-play-with-BPF-ring-.patch | 147 ++++++++++++++++
>  Documentation/bpf/ringbuf.rst                 |  18 +-
>  include/uapi/linux/bpf.h                      |   3 +
>  kernel/bpf/ringbuf.c                          |  43 +++--
>  tools/include/uapi/linux/bpf.h                |   3 +
>  tools/lib/bpf/ringbuf.c                       | 106 ++++++++++++
>  tools/testing/selftests/bpf/Makefile          |   5 +-
>  .../bpf/prog_tests/ringbuf_overwritable.c     | 158 ++++++++++++++++++
>  .../bpf/progs/test_ringbuf_overwritable.c     |  61 +++++++
>  9 files changed, 531 insertions(+), 13 deletions(-)
>  create mode 100644 0001-for-test-purpose-only-Add-toy-to-play-with-BPF-ring-.patch
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/ringbuf_overwritable.c
>  create mode 100644 tools/testing/selftests/bpf/progs/test_ringbuf_overwritable.c
>
>
> Best regards and thank you in advance.
> ---
> [1] https://github.com/kinvolk/traceloop
> Traceloop was presented at LPC 2020 (https://lpc.events/event/7/contributions/667/)
> --
> 2.25.1
>