All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Toke Høiland-Jørgensen" <toke@redhat.com>
To: Daniel Borkmann <daniel@iogearbox.net>,
	Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>,
	netdev@vger.kernel.org, bpf@vger.kernel.org
Subject: Re: [PATCH bpf v2] bpf: fix potential race in tail call compatibility check
Date: Tue, 26 Oct 2021 01:16:45 +0200	[thread overview]
Message-ID: <878rygbspu.fsf@toke.dk> (raw)
In-Reply-To: <c1244c73-bc61-89b8-dca3-f06dca85f64e@iogearbox.net>

Daniel Borkmann <daniel@iogearbox.net> writes:

> On 10/25/21 4:28 PM, Lorenzo Bianconi wrote:
>>> Lorenzo noticed that the code testing for program type compatibility of
>>> tail call maps is potentially racy in that two threads could encounter a
>>> map with an unset type simultaneously and both return true even though they
>>> are inserting incompatible programs.
>>>
>>> The race window is quite small, but artificially enlarging it by adding a
>>> usleep_range() inside the check in bpf_prog_array_compatible() makes it
>>> trivial to trigger from userspace with a program that does, essentially:
>>>
>>>          map_fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, 4, 4, 2, 0);
>>>          pid = fork();
>>>          if (pid) {
>>>                  key = 0;
>>>                  value = xdp_fd;
>>>          } else {
>>>                  key = 1;
>>>                  value = tc_fd;
>>>          }
>>>          err = bpf_map_update_elem(map_fd, &key, &value, 0);
>>>
>>> While the race window is small, it has potentially serious ramifications in
>>> that triggering it would allow a BPF program to tail call to a program of a
>>> different type. So let's get rid of it by protecting the update with a
>>> spinlock. The commit in the Fixes tag is the last commit that touches the
>>> code in question.
>>>
>>> v2:
>>> - Use a spinlock instead of an atomic variable and cmpxchg() (Alexei)
>>>
>>> Fixes: 3324b584b6f6 ("ebpf: misc core cleanup")
>>> Reported-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
>>> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
>>> ---
>>>   include/linux/bpf.h   |  1 +
>>>   kernel/bpf/arraymap.c |  1 +
>>>   kernel/bpf/core.c     | 14 ++++++++++----
>>>   kernel/bpf/syscall.c  |  2 ++
>>>   4 files changed, 14 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>>> index 020a7d5bf470..98d906176d89 100644
>>> --- a/include/linux/bpf.h
>>> +++ b/include/linux/bpf.h
>>> @@ -929,6 +929,7 @@ struct bpf_array_aux {
>>>   	 * stored in the map to make sure that all callers and callees have
>>>   	 * the same prog type and JITed flag.
>>>   	 */
>>> +	spinlock_t type_check_lock;
>> 
>> I was wondering if we can use a mutex instead of a spinlock here since it is
>> run from a syscall AFAIU. The only downside is mutex_lock is run inside
>> aux->used_maps_mutex critical section. Am I missing something?
>
> Hm, potentially it could work, but then it's also 32 vs 4 extra bytes. There's
> also poke_mutex or freeze_mutex, but feels to hacky to 'generalize for reuse',
> so I think the spinlock in bpf_array_aux is fine.
>
>>>   	enum bpf_prog_type type;
>>>   	bool jited;
>>>   	/* Programs with direct jumps into programs part of this array. */
>>> diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
>>> index cebd4fb06d19..da9b1e96cadc 100644
>>> --- a/kernel/bpf/arraymap.c
>>> +++ b/kernel/bpf/arraymap.c
>>> @@ -1072,6 +1072,7 @@ static struct bpf_map *prog_array_map_alloc(union bpf_attr *attr)
>>>   	INIT_WORK(&aux->work, prog_array_map_clear_deferred);
>>>   	INIT_LIST_HEAD(&aux->poke_progs);
>>>   	mutex_init(&aux->poke_mutex);
>>> +	spin_lock_init(&aux->type_check_lock);
>
> Just as a tiny nit, I would probably name it slightly different, since type_check_lock
> mainly refers to the type property but there's also jit vs non-jit and as pointed out
> there could be other extensions that need checking in future as well. Maybe 'compat_lock'
> would be a more generic one or just:
>
>          struct {
>                  enum bpf_prog_type type;
>                  bool jited;
>                  spinlock_t lock;
>          } owner;

Uh, I like that! Makes it easier to move as well (which we're doing as
part of the xdp_mb series). Will send a v3 with this :)

-Toke


  reply	other threads:[~2021-10-25 23:16 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-25 13:08 [PATCH bpf v2] bpf: fix potential race in tail call compatibility check Toke Høiland-Jørgensen
2021-10-25 14:28 ` Lorenzo Bianconi
2021-10-25 22:04   ` Daniel Borkmann
2021-10-25 23:16     ` Toke Høiland-Jørgensen [this message]
2021-10-26  8:18     ` Lorenzo Bianconi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=878rygbspu.fsf@toke.dk \
    --to=toke@redhat.com \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=lorenzo.bianconi@redhat.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.