Re: [PATCH bpf-next v8 0/5] bpf: Add open-coded style process file iterator and bpf_fget_task() kfunc

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I noticed an interesting problem in Kernel CI.

For gcc-compiled kernel (x86_64-gcc, aarch64-gcc, s390x-gcc), the
iters/task_file test case can pass.

But for llvm-compiled kernel (x86_64-llvm-17, x86_64-llvm-18), the
iters/task_file test case fails.
The following is the error:

; if (task->parent->pid != parent_pid) @ iters_task_file.c:26
1: (79) r1 = *(u64 *)(r0 +1696)       ; R0_w=trusted_ptr_task_struct()
R1_w=rcu_ptr_or_null_task_struct(id=1)
2: (61) r1 = *(u32 *)(r1 +1672)
R1 invalid mem access 'rcu_ptr_or_null_'

I reproduced this error on my local laptop. With the same ebpf program,
it can be loaded normally into gcc-compiled kernel, but loading it into
llvm-compiled (make LLVM=1) kernel will result in an error.

The behavior of the gcc-compiled kernel is inconsistent with the
llvm-compiled kernel.

After my analysis, this is because of the following reasons:

Currently, llvm-compiled kernel can determine whether a pointer is rcu
or rcu_or_null based on btf_type_tag and allowlist, but gcc-compiled
kernel can only based on allowlist, because gcc does not support
btf_type_tag (commit 6fcd486b3a0a "bpf: Refactor RCU enforcement
in the verifier.").

Since the verifier cannot determine which fields are safe to read, it is
not feasible to set all untagged fields to PTR_UNTRUSTED, so just remove
the PTR_TRUSTED (commit afeebf9f57a4 "bpf: Undo strict enforcement for
walking untagged fields.").

These end up leading to an interesting problem.

static int check_ptr_to_btf_access(...)
{
...
   } else if (in_rcu_cs(env) && !type_may_be_null(reg->type)) {
      if (type_is_rcu(env, reg, field_name, btf_id)) {   (1)
         /* ignore __rcu tag and mark it MEM_RCU */
         flag |= MEM_RCU;
      } else if (flag & MEM_RCU ||                       (2)
         type_is_rcu_or_null(env, reg, field_name, btf_id)) {
         /* __rcu tagged pointers can be NULL */
         flag |= MEM_RCU | PTR_MAYBE_NULL;

         /* We always trust them */
         if (type_is_rcu_or_null(env, reg, field_name, btf_id) &&
            flag & PTR_UNTRUSTED)
               flag &= ~PTR_UNTRUSTED;
      } else if (flag & (MEM_PERCPU | MEM_USER)) {       (3)
         /* keep as-is */
      } else {                                           (4)
      /* walking unknown pointers yields old deprecated PTR_TO_BTF_ID */
         clear_trusted_flags(&flag);
      }
...
}

In gcc-compiled kernel, since task->parent is not in BTF_TYPE_SAFE_RCU
and BTF_TYPE_SAFE_RCU_OR_NULL, and since btf_type_tag is not supported,
there is no MEM_RCU, branch (4) is reached, just remove PTR_TRUSTED.
Although we cannot pass such a pointer to a kfunc with KF_TRUSTED_ARGS
or KF_RCU, we can still dereference it.

In llvm-compiled kernel, since task->parent is tagged with __rcu,
branch (2) is reached and flag is set to MEM_RCU | PTR_MAYBE_NULL.
This results in the "R1 invalid mem access rcu_ptr_or_null_" error
mentioned earlier.

If we remove the __rcu tag of task->parent, then there is no error,
even with the llvm-compiled kernel, because branch (4) is reached like
the gcc-compiled kernel.

This leads to the interesting result that pointers not tagged with __rcu
will have more freedom to dereference than pointers tagged with __rcu.

This result is obviously incorrect because the pointer may be NULL
whether or not it is tagged with __rcu.

But how to fix this problem seems a bit tricky.

It is not possible to mark every data structure member whether it may
be NULL.

If every pointer has PTR_MAYBE_NULL by default, chained dereferencing
will become cumbersome.

More discussion is welcome.

Many thanks.




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux