Skip to content

Commit

Permalink
selftests/bpf: Add lazy global subprog validation tests
Browse files Browse the repository at this point in the history
JIRA: https://issues.redhat.com/browse/RHEL-23644

commit e8a339b5235e294f29153149ea7cf26a9a87dbea
Author: Andrii Nakryiko <andrii@kernel.org>
Date:   Thu Nov 23 19:59:37 2023 -0800

    selftests/bpf: Add lazy global subprog validation tests
    
    Add a few test that validate BPF verifier's lazy approach to validating
    global subprogs.
    
    We check that global subprogs that are called transitively through
    another global subprog is validated.
    
    We also check that invalid global subprog is not validated, if it's not
    called from the main program.
    
    And we also check that main program is always validated first, before
    any of the subprogs.
    
    Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
    Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
    Acked-by: Eduard Zingerman <eddyz87@gmail.com>
    Acked-by: Daniel Borkmann <daniel@iogearbox.net>
    Link: https://lore.kernel.org/bpf/20231124035937.403208-4-andrii@kernel.org

Signed-off-by: Viktor Malik <vmalik@redhat.com>
  • Loading branch information
viktormalik committed Apr 30, 2024
1 parent 7348bbb commit c2da7eb
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 0 deletions.
2 changes: 2 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "verifier_direct_stack_access_wraparound.skel.h"
#include "verifier_div0.skel.h"
#include "verifier_div_overflow.skel.h"
#include "verifier_global_subprogs.skel.h"
#include "verifier_gotol.skel.h"
#include "verifier_helper_access_var_len.skel.h"
#include "verifier_helper_packet_access.skel.h"
Expand Down Expand Up @@ -134,6 +135,7 @@ void test_verifier_direct_packet_access(void) { RUN(verifier_direct_packet_acces
void test_verifier_direct_stack_access_wraparound(void) { RUN(verifier_direct_stack_access_wraparound); }
void test_verifier_div0(void) { RUN(verifier_div0); }
void test_verifier_div_overflow(void) { RUN(verifier_div_overflow); }
void test_verifier_global_subprogs(void) { RUN(verifier_global_subprogs); }
void test_verifier_gotol(void) { RUN(verifier_gotol); }
void test_verifier_helper_access_var_len(void) { RUN(verifier_helper_access_var_len); }
void test_verifier_helper_packet_access(void) { RUN(verifier_helper_packet_access); }
Expand Down
92 changes: 92 additions & 0 deletions tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */

#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"

int arr[1];
int unkn_idx;

__noinline long global_bad(void)
{
return arr[unkn_idx]; /* BOOM */
}

__noinline long global_good(void)
{
return arr[0];
}

__noinline long global_calls_bad(void)
{
return global_good() + global_bad() /* does BOOM indirectly */;
}

__noinline long global_calls_good_only(void)
{
return global_good();
}

SEC("?raw_tp")
__success __log_level(2)
/* main prog is validated completely first */
__msg("('global_calls_good_only') is global and assumed valid.")
__msg("1: (95) exit")
/* eventually global_good() is transitively validated as well */
__msg("Validating global_good() func")
__msg("('global_good') is safe for any args that match its prototype")
int chained_global_func_calls_success(void)
{
return global_calls_good_only();
}

SEC("?raw_tp")
__failure __log_level(2)
/* main prog validated successfully first */
__msg("1: (95) exit")
/* eventually we validate global_bad() and fail */
__msg("Validating global_bad() func")
__msg("math between map_value pointer and register") /* BOOM */
int chained_global_func_calls_bad(void)
{
return global_calls_bad();
}

/* do out of bounds access forcing verifier to fail verification if this
* global func is called
*/
__noinline int global_unsupp(const int *mem)
{
if (!mem)
return 0;
return mem[100]; /* BOOM */
}

const volatile bool skip_unsupp_global = true;

SEC("?raw_tp")
__success
int guarded_unsupp_global_called(void)
{
if (!skip_unsupp_global)
return global_unsupp(NULL);
return 0;
}

SEC("?raw_tp")
__failure __log_level(2)
__msg("Func#1 ('global_unsupp') is global and assumed valid.")
__msg("Validating global_unsupp() func#1...")
__msg("value is outside of the allowed memory range")
int unguarded_unsupp_global_called(void)
{
int x = 0;

return global_unsupp(&x);
}

char _license[] SEC("license") = "GPL";

0 comments on commit c2da7eb

Please sign in to comment.