[Devel] [PATCH VZ10 3/3] selftests/ve_devcg_bpf: test orig_insns reporting via BPF_OBJ_GET_INFO_BY_FD

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Wed Jul 29 14:39:36 MSK 2026



On 7/29/26 11:47, Vasileios Almpanis wrote:
> 
> On 7/28/26 1:08 PM, Pavel Tikhomirov wrote:
>> Add prog_info_orig_host: from privileged (bpf_capable) host context, load
>> a CGROUP_DEVICE program and verify BPF_OBJ_GET_INFO_BY_FD reports its
>> original instructions - orig_prog_len equals the program size and, when
>> dumping is allowed, orig_prog_insns round-trips byte for byte. This is
>> the interface CRIU uses to dump the program.
>>
>> Only a host test is provided: a VE process is not bpf_capable(), so
>> bpf_prog_get_info_by_fd() zeroes the program-info lengths and returns
>> early, and orig_prog info is never exposed to the VE itself. CRIU dumps
>> from the host, which is the path exercised here.

>> The kptr_restrict is
>> pinned to make bpf_dump_raw_ok() deterministic and restored afterwards.

> Where is kptr_restrict pinned? I don't see it in the test. Also orig_insns is not gated
> on bpf_dump_raw_ok. Only on bpf_capable(). I think this message part is just stale
> because we only reset orig_prog_len in capable checks unlike other fields that gate on
> bpf_dump_raw_ok. Apart from that LGTM

You are right, this last sentence is a leftover blooper from the version where I
experimented with kptr_restrict. We don't really need extra restriction as we don't have
any kernel-private pointers in the original program (the converted (xlated/jitted)
have this extra restriction via bpf_dump_raw_ok()).

>>
>> Feature: BPF checkpoint/restore
>> https://virtuozzo.atlassian.net/browse/VSTOR-121776
>> Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
>> ---
>>   tools/testing/selftests/ve_devcg_bpf/Makefile |  3 ++
>>   .../ve_devcg_bpf/ve_devcg_bpf_test.c          | 50 +++++++++++++++++++
>>   2 files changed, 53 insertions(+)
>>
>> diff --git a/tools/testing/selftests/ve_devcg_bpf/Makefile b/tools/testing/selftests/ve_devcg_bpf/Makefile
>> index c5aa8b59880f3..ba811f22e36ac 100644
>> --- a/tools/testing/selftests/ve_devcg_bpf/Makefile
>> +++ b/tools/testing/selftests/ve_devcg_bpf/Makefile
>> @@ -1,6 +1,9 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   # Makefile for ve_devcg_bpf selftests.
>>   CFLAGS += -g -Wall -O2
>> +# orig_prog_len/orig_prog_insns may be newer than the system UAPI headers, so
>> +# build against the in-tree copy under tools/include/uapi.
>> +CFLAGS += $(TOOLS_INCLUDES)
>>     TEST_GEN_PROGS += ve_devcg_bpf_test
>>   diff --git a/tools/testing/selftests/ve_devcg_bpf/ve_devcg_bpf_test.c b/tools/testing/selftests/ve_devcg_bpf/ve_devcg_bpf_test.c
>> index da55f82492f5b..cca0f6905d8da 100644
>> --- a/tools/testing/selftests/ve_devcg_bpf/ve_devcg_bpf_test.c
>> +++ b/tools/testing/selftests/ve_devcg_bpf/ve_devcg_bpf_test.c
>> @@ -12,6 +12,8 @@
>>    *   on the VE root cgroup.
>>    * - BPF_PROG_TYPE_CGROUP_DEVICE programs can be attached to and queried
>>    *   on descendant cgroups inside VE.
>> + * - BPF_OBJ_GET_INFO_BY_FD reports a CGROUP_DEVICE program's original
>> + *   instructions (orig_prog_len/orig_prog_insns) for CRIU dump/restore.
>>    */
>>   #define _GNU_SOURCE
>>   #include <linux/sched.h>
>> @@ -739,4 +741,52 @@ TEST_F(ve_devcg_bpf, prog_load_oversized_denied)
>>                  TEST_PROG_LOAD_OVERSIZED_DENIED), 0);
>>   }
>>   +/*
>> + * From host (root) context (hence no fixture in this test), dump the program
>> + * and check it matches the loaded version exactly.
>> + */
>> +TEST(prog_info_orig_host)
>> +{
>> +    struct bpf_insn insns[] = { DEVCG_PROG_INSNS };
>> +    struct bpf_insn readback[ARRAY_SIZE(insns)];
>> +    struct bpf_prog_info info;
>> +    union bpf_attr attr;
>> +    int prog_fd;
>> +
>> +    prog_fd = load_devcg_prog();
>> +    ASSERT_GE(prog_fd, 0);
>> +
>> +    /* Size query: orig_prog_len must equal the loaded program size. */
>> +    memset(&info, 0, sizeof(info));
>> +    memset(&attr, 0, sizeof(attr));
>> +    attr.info.bpf_fd = prog_fd;
>> +    attr.info.info_len = sizeof(info);
>> +    attr.info.info = (unsigned long)&info;
>> +    ASSERT_EQ(syscall(__NR_bpf, BPF_OBJ_GET_INFO_BY_FD, &attr,
>> +              sizeof(attr)), 0);
>> +
>> +    if (info.orig_prog_len == 0) {
>> +        close(prog_fd);
>> +        SKIP(return, "kernel does not report orig_insns");
>> +    }
>> +    ASSERT_EQ(info.orig_prog_len, sizeof(insns));
>> +
>> +    /* The original instructions round-trip byte for byte. */
>> +    memset(&info, 0, sizeof(info));
>> +    memset(readback, 0xff, sizeof(readback));
>> +    info.orig_prog_len = sizeof(readback);
>> +    info.orig_prog_insns = (unsigned long)readback;
>> +    memset(&attr, 0, sizeof(attr));
>> +    attr.info.bpf_fd = prog_fd;
>> +    attr.info.info_len = sizeof(info);
>> +    attr.info.info = (unsigned long)&info;
>> +    EXPECT_EQ(syscall(__NR_bpf, BPF_OBJ_GET_INFO_BY_FD, &attr,
>> +              sizeof(attr)), 0);
>> +    EXPECT_EQ(info.orig_prog_len, sizeof(insns));
>> +    EXPECT_NE(info.orig_prog_insns, 0);
>> +    EXPECT_EQ(memcmp(readback, insns, sizeof(insns)), 0);
>> +
>> +    close(prog_fd);
>> +}
>> +
>>   TEST_HARNESS_MAIN
> 

-- 
Best regards, Pavel Tikhomirov
Senior Software Developer, Virtuozzo.



More information about the Devel mailing list