root/tools/testing/selftests/bpf/prog_tests/percpu_array_inner_map.c
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>

/*
 * Test that replacing an inner percpu array map with one that has different
 * max_entries is rejected.  percpu_array_map_gen_lookup() inlines the
 * template's index_mask, so allowing a smaller replacement would cause OOB.
 */
void test_percpu_array_inner_map(void)
{
        LIBBPF_OPTS(bpf_map_create_opts, opts);
        int outer_fd, tmpl_fd, good_fd, bad_fd, err;
        int zero = 0;

        /* Create template: percpu array with 8 entries */
        tmpl_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "tmpl",
                                 sizeof(int), sizeof(long), 8, NULL);
        if (!ASSERT_OK_FD(tmpl_fd, "create_tmpl"))
                return;

        /* Create outer array-of-maps using template */
        opts.inner_map_fd = tmpl_fd;
        outer_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY_OF_MAPS, "outer",
                                  sizeof(int), sizeof(int), 1, &opts);
        if (!ASSERT_OK_FD(outer_fd, "create_outer"))
                goto close_tmpl;

        /* Insert template as initial inner map */
        err = bpf_map_update_elem(outer_fd, &zero, &tmpl_fd, 0);
        if (!ASSERT_OK(err, "insert_tmpl"))
                goto close_outer;

        /* Replacement with same max_entries should succeed */
        good_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "good",
                                 sizeof(int), sizeof(long), 8, NULL);
        if (!ASSERT_OK_FD(good_fd, "create_good"))
                goto close_outer;

        err = bpf_map_update_elem(outer_fd, &zero, &good_fd, 0);
        ASSERT_OK(err, "replace_same_max_entries");
        close(good_fd);

        /* Replacement with fewer max_entries must fail */
        bad_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "bad",
                                sizeof(int), sizeof(long), 2, NULL);
        if (!ASSERT_OK_FD(bad_fd, "create_bad"))
                goto close_outer;

        err = bpf_map_update_elem(outer_fd, &zero, &bad_fd, 0);
        ASSERT_ERR(err, "replace_smaller_max_entries");
        close(bad_fd);

close_outer:
        close(outer_fd);
close_tmpl:
        close(tmpl_fd);
}