From: Sandipan Das <hidden> Date: 2018-05-18 12:50:50
This patch series introduces the following:
[1] Support for bpf-to-bpf function calls in the powerpc64 JIT compiler.
[2] Provide a way for resolving function calls because of the way JITed
images are allocated in powerpc64.
[3] Fix to get JITed instruction dumps for multi-function programs from
the bpf system call.
v2:
- Incorporate review comments from Jakub
Sandipan Das (6):
bpf: support 64-bit offsets for bpf function calls
bpf: powerpc64: add JIT support for multi-function programs
bpf: get kernel symbol addresses via syscall
tools: bpf: sync bpf uapi header
tools: bpftool: resolve calls without using imm field
bpf: fix JITed dump for multi-function programs via syscall
arch/powerpc/net/bpf_jit_comp64.c | 79 ++++++++++++++++++++++++++++++++++-----
include/uapi/linux/bpf.h | 2 +
kernel/bpf/syscall.c | 56 ++++++++++++++++++++++++---
kernel/bpf/verifier.c | 22 +++++++----
tools/bpf/bpftool/prog.c | 29 ++++++++++++++
tools/bpf/bpftool/xlated_dumper.c | 10 ++++-
tools/bpf/bpftool/xlated_dumper.h | 2 +
tools/include/uapi/linux/bpf.h | 2 +
8 files changed, 179 insertions(+), 23 deletions(-)
--
2.14.3
From: Sandipan Das <hidden> Date: 2018-05-18 12:50:52
The imm field of a bpf instruction is a signed 32-bit integer.
For JIT bpf-to-bpf function calls, it stores the offset of the
start address of the callee's JITed image from __bpf_call_base.
For some architectures, such as powerpc64, this offset may be
as large as 64 bits and cannot be accomodated in the imm field
without truncation.
We resolve this by:
[1] Additionally using the auxillary data of each function to
keep a list of start addresses of the JITed images for all
functions determined by the verifier.
[2] Retaining the subprog id inside the off field of the call
instructions and using it to index into the list mentioned
above and lookup the callee's address.
To make sure that the existing JIT compilers continue to work
without requiring changes, we keep the imm field as it is.
Signed-off-by: Sandipan Das <redacted>
---
kernel/bpf/verifier.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
@@ -5383,11 +5383,24 @@ static int jit_subprogs(struct bpf_verifier_env *env)insn->src_reg!=BPF_PSEUDO_CALL)continue;subprog=insn->off;-insn->off=0;insn->imm=(u64(*)(u64,u64,u64,u64,u64))func[subprog]->bpf_func-__bpf_call_base;}++/* we use the aux data to keep a list of the start addresses+*oftheJITedimagesforeachfunctionintheprogram+*+*forsomearchitectures,suchaspowerpc64,theimmfield+*mightnotbelargeenoughtoholdtheoffsetofthestart+*addressofthecallee'sJITedimagefrom__bpf_call_base+*+*insuchcases,wecanlookupthestartaddressofacallee+*byusingitssubprogid,availablefromtheofffieldof+*thecallinstruction,asanindexforthislist+*/+func[i]->aux->func=func;+func[i]->aux->func_cnt=env->subprog_cnt+1;}for(i=0;i<env->subprog_cnt;i++){old_bpf_func=func[i]->bpf_func;
From: Sandipan Das <hidden> Date: 2018-05-18 12:50:55
This adds support for bpf-to-bpf function calls in the powerpc64
JIT compiler. The JIT compiler converts the bpf call instructions
to native branch instructions. After a round of the usual passes,
the start addresses of the JITed images for the callee functions
are known. Finally, to fixup the branch target addresses, we need
to perform an extra pass.
Because of the address range in which JITed images are allocated
on powerpc64, the offsets of the start addresses of these images
from __bpf_call_base are as large as 64 bits. So, for a function
call, we cannot use the imm field of the instruction to determine
the callee's address. Instead, we use the alternative method of
getting it from the list of function addresses in the auxillary
data of the caller by using the off field as an index.
Signed-off-by: Sandipan Das <redacted>
---
arch/powerpc/net/bpf_jit_comp64.c | 79 ++++++++++++++++++++++++++++++++++-----
1 file changed, 69 insertions(+), 10 deletions(-)
@@ -256,7 +256,7 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32/* Assemble the body code between the prologue & epilogue */staticintbpf_jit_build_body(structbpf_prog*fp,u32*image,structcodegen_context*ctx,-u32*addrs)+u32*addrs,boolextra_pass){conststructbpf_insn*insn=fp->insnsi;intflen=fp->len;
@@ -712,11 +712,23 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,break;/*-*Callkernelhelper+*Callkernelhelperorbpffunction*/caseBPF_JMP|BPF_CALL:ctx->seen|=SEEN_FUNC;-func=(u8*)__bpf_call_base+imm;++/* bpf function call */+if(insn[i].src_reg==BPF_PSEUDO_CALL&&extra_pass)+if(fp->aux->func&&off<fp->aux->func_cnt)+/* use the subprog id from the off+*fieldtolookupthecalleeaddress+*/+func=(u8*)fp->aux->func[off]->bpf_func;+else+return-EINVAL;+/* kernel helper call */+else+func=(u8*)__bpf_call_base+imm;bpf_jit_emit_func_call(image,ctx,(u64)func);
@@ -935,7 +979,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)/* Now build the prologue, body code & epilogue for real. */cgctx.idx=0;bpf_jit_build_prologue(code_base,&cgctx);-bpf_jit_build_body(fp,code_base,&cgctx,addrs);+bpf_jit_build_body(fp,code_base,&cgctx,addrs,extra_pass);bpf_jit_build_epilogue(code_base,&cgctx);if(bpf_jit_enable>1)
From: Sandipan Das <hidden> Date: 2018-05-18 12:50:56
This adds new two new fields to struct bpf_prog_info. For
multi-function programs, these fields can be used to pass
a list of kernel symbol addresses for all functions in a
given program and to userspace using the bpf system call
with the BPF_OBJ_GET_INFO_BY_FD command.
When bpf_jit_kallsyms is enabled, we can get the address
of the corresponding kernel symbol for a callee function
and resolve the symbol's name. The address is determined
by adding the value of the call instruction's imm field
to __bpf_call_base. This offset gets assigned to the imm
field by the verifier.
For some architectures, such as powerpc64, the imm field
is not large enough to hold this offset.
We resolve this by:
[1] Assigning the subprog id to the imm field of a call
instruction in the verifier instead of the offset of
the callee's symbol's address from __bpf_call_base.
[2] Determining the address of a callee's corresponding
symbol by using the imm field as an index for the
list of kernel symbol addresses now available from
the program info.
Suggested-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Sandipan Das <redacted>
---
include/uapi/linux/bpf.h | 2 ++
kernel/bpf/syscall.c | 20 ++++++++++++++++++++
kernel/bpf/verifier.c | 7 +------
3 files changed, 23 insertions(+), 6 deletions(-)
@@ -1933,6 +1933,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,if(!capable(CAP_SYS_ADMIN)){info.jited_prog_len=0;info.xlated_prog_len=0;+info.nr_jited_ksyms=0;gotodone;}
@@ -1981,6 +1982,25 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,}}+ulen=info.nr_jited_ksyms;+info.nr_jited_ksyms=prog->aux->func_cnt;+if(info.nr_jited_ksyms&&ulen){+u64__user*user_jited_ksyms=u64_to_user_ptr(info.jited_ksyms);+ulongksym_addr;+u32i;++/* copy the address of the kernel symbol corresponding to+*eachfunction+*/+ulen=min_t(u32,info.nr_jited_ksyms,ulen);+for(i=0;i<ulen;i++){+ksym_addr=(ulong)prog->aux->func[i]->bpf_func;+ksym_addr&=PAGE_MASK;+if(put_user((u64)ksym_addr,&user_jited_ksyms[i]))+return-EFAULT;+}+}+done:if(copy_to_user(uinfo,&info,info_len)||put_user(info_len,&uattr->info.info_len))
From: Sandipan Das <hidden> Date: 2018-05-18 12:50:59
Syncing the bpf.h uapi header with tools so that struct
bpf_prog_info has the two new fields for passing on the
addresses of the kernel symbols corresponding to each
function in a JITed program.
Signed-off-by: Sandipan Das <redacted>
---
tools/include/uapi/linux/bpf.h | 2 ++
1 file changed, 2 insertions(+)
From: Sandipan Das <hidden> Date: 2018-05-18 12:51:01
Currently, we resolve the callee's address for a JITed function
call by using the imm field of the call instruction as an offset
from __bpf_call_base. If bpf_jit_kallsyms is enabled, we further
use this address to get the callee's kernel symbol's name.
For some architectures, such as powerpc64, the imm field is not
large enough to hold this offset. So, instead of assigning this
offset to the imm field, the verifier now assigns the subprog
id. Also, a list of kernel symbol addresses for all the JITed
functions is provided in the program info. We now use the imm
field as an index for this list to lookup a callee's symbol's
address and resolve its name.
Suggested-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Sandipan Das <redacted>
---
v2:
- Order variables from longest to shortest
- Make sure that ksyms_ptr and ksyms_len are always initialized
- Simplify code
---
tools/bpf/bpftool/prog.c | 29 +++++++++++++++++++++++++++++
tools/bpf/bpftool/xlated_dumper.c | 10 +++++++++-
tools/bpf/bpftool/xlated_dumper.h | 2 ++
3 files changed, 40 insertions(+), 1 deletion(-)
@@ -174,7 +174,11 @@ static const char *print_call_pcrel(struct dump_data *dd,unsignedlongaddress,conststructbpf_insn*insn){-if(sym)+if(!dd->nr_jited_ksyms)+/* Do not show address for interpreted programs */+snprintf(dd->scratch_buff,sizeof(dd->scratch_buff),+"%+d",insn->off);+elseif(sym)snprintf(dd->scratch_buff,sizeof(dd->scratch_buff),"%+d#%s",insn->off,sym->name);else
From: Sandipan Das <hidden> Date: 2018-05-18 12:51:04
Currently, for multi-function programs, we cannot get the JITed
instructions using the bpf system call's BPF_OBJ_GET_INFO_BY_FD
command. Because of this, userspace tools such as bpftool fail
to identify a multi-function program as being JITed or not.
With the JIT enabled and the test program running, this can be
verified as follows:
# cat /proc/sys/net/core/bpf_jit_enable
1
Before applying this patch:
# bpftool prog list
1: kprobe name foo tag b811aab41a39ad3d gpl
loaded_at 2018-05-16T11:43:38+0530 uid 0
xlated 216B not jited memlock 65536B
...
# bpftool prog dump jited id 1
no instructions returned
After applying this patch:
# bpftool prog list
1: kprobe name foo tag b811aab41a39ad3d gpl
loaded_at 2018-05-16T12:13:01+0530 uid 0
xlated 216B jited 308B memlock 65536B
...
# bpftool prog dump jited id 1
0: nop
4: nop
8: mflr r0
c: std r0,16(r1)
10: stdu r1,-112(r1)
14: std r31,104(r1)
18: addi r31,r1,48
1c: li r3,10
...
Signed-off-by: Sandipan Das <redacted>
---
kernel/bpf/syscall.c | 38 ++++++++++++++++++++++++++++++++------
1 file changed, 32 insertions(+), 6 deletions(-)
@@ -1896,7 +1896,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,structbpf_prog_infoinfo={};u32info_len=attr->info.info_len;char__user*uinsns;-u32ulen;+u32ulen,i;interr;err=check_uarg_tail_zero(uinfo,sizeof(info),info_len);
@@ -1922,7 +1922,6 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,ulen=min_t(u32,info.nr_map_ids,ulen);if(ulen){u32__user*user_map_ids=u64_to_user_ptr(info.map_ids);-u32i;for(i=0;i<ulen;i++)if(put_user(prog->aux->used_maps[i]->id,
@@ -1970,13 +1969,41 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,*foroffload.*/ulen=info.jited_prog_len;-info.jited_prog_len=prog->jited_len;+if(prog->aux->func_cnt){+info.jited_prog_len=0;+for(i=0;i<prog->aux->func_cnt;i++)+info.jited_prog_len+=prog->aux->func[i]->jited_len;+}else{+info.jited_prog_len=prog->jited_len;+}+if(info.jited_prog_len&&ulen){if(bpf_dump_raw_ok()){uinsns=u64_to_user_ptr(info.jited_prog_insns);ulen=min_t(u32,info.jited_prog_len,ulen);-if(copy_to_user(uinsns,prog->bpf_func,ulen))-return-EFAULT;++/* for multi-function programs, copy the JITed+*instructionsforallthefunctions+*/+if(prog->aux->func_cnt){+u32len,free;+u8*img;++free=ulen;+for(i=0;i<prog->aux->func_cnt;i++){+len=prog->aux->func[i]->jited_len;+img=(u8*)prog->aux->func[i]->bpf_func;+if(len>free)+break;+if(copy_to_user(uinsns,img,len))+return-EFAULT;+uinsns+=len;+free-=len;+}+}else{+if(copy_to_user(uinsns,prog->bpf_func,ulen))+return-EFAULT;+}}else{info.jited_prog_insns=0;}
@@ -1987,7 +2014,6 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,if(info.nr_jited_ksyms&&ulen){u64__user*user_jited_ksyms=u64_to_user_ptr(info.jited_ksyms);ulongksym_addr;-u32i;/* copy the address of the kernel symbol corresponding to*eachfunction
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-05-18 15:30:41
On 05/18/2018 02:50 PM, Sandipan Das wrote:
quoted hunk
This adds support for bpf-to-bpf function calls in the powerpc64
JIT compiler. The JIT compiler converts the bpf call instructions
to native branch instructions. After a round of the usual passes,
the start addresses of the JITed images for the callee functions
are known. Finally, to fixup the branch target addresses, we need
to perform an extra pass.
Because of the address range in which JITed images are allocated
on powerpc64, the offsets of the start addresses of these images
from __bpf_call_base are as large as 64 bits. So, for a function
call, we cannot use the imm field of the instruction to determine
the callee's address. Instead, we use the alternative method of
getting it from the list of function addresses in the auxillary
data of the caller by using the off field as an index.
Signed-off-by: Sandipan Das <redacted>
---
arch/powerpc/net/bpf_jit_comp64.c | 79 ++++++++++++++++++++++++++++++++++-----
1 file changed, 69 insertions(+), 10 deletions(-)
@@ -256,7 +256,7 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32/* Assemble the body code between the prologue & epilogue */staticintbpf_jit_build_body(structbpf_prog*fp,u32*image,structcodegen_context*ctx,-u32*addrs)+u32*addrs,boolextra_pass){conststructbpf_insn*insn=fp->insnsi;intflen=fp->len;
@@ -712,11 +712,23 @@ static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,break;/*-*Callkernelhelper+*Callkernelhelperorbpffunction*/caseBPF_JMP|BPF_CALL:ctx->seen|=SEEN_FUNC;-func=(u8*)__bpf_call_base+imm;++/* bpf function call */+if(insn[i].src_reg==BPF_PSEUDO_CALL&&extra_pass)
Perhaps it might make sense here for !extra_pass to set func to some dummy
address as otherwise the 'kernel helper call' branch used for this is a bit
misleading in that sense. The PPC_LI64() used in bpf_jit_emit_func_call()
optimizes the immediate addr, I presume the JIT can handle situations where
in the final extra_pass the image needs to grow/shrink again (due to different
final address for the call)?
quoted hunk
+ if (fp->aux->func && off < fp->aux->func_cnt)
+ /* use the subprog id from the off
+ * field to lookup the callee address
+ */
+ func = (u8 *) fp->aux->func[off]->bpf_func;
+ else
+ return -EINVAL;
+ /* kernel helper call */
+ else
+ func = (u8 *) __bpf_call_base + imm;
bpf_jit_emit_func_call(image, ctx, (u64)func);
powerpc doesn't implement set_memory_ro(). Generally this is not a problem since
set_memory_ro() defaults to 'return 0' in this case, but since the bpf_jit_free()
destructor is overridden here, there's no bpf_jit_binary_unlock_ro() and in case
powerpc would get set_memory_*() support one day this will then crash in random
places once the mem gets back to the allocator, thus hard to debug. Two options:
either you remove the bpf_jit_free() override or you remove the bpf_jit_binary_lock_ro().
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-05-18 15:42:04
On 05/18/2018 02:50 PM, Sandipan Das wrote:
quoted hunk
The imm field of a bpf instruction is a signed 32-bit integer.
For JIT bpf-to-bpf function calls, it stores the offset of the
start address of the callee's JITed image from __bpf_call_base.
For some architectures, such as powerpc64, this offset may be
as large as 64 bits and cannot be accomodated in the imm field
without truncation.
We resolve this by:
[1] Additionally using the auxillary data of each function to
keep a list of start addresses of the JITed images for all
functions determined by the verifier.
[2] Retaining the subprog id inside the off field of the call
instructions and using it to index into the list mentioned
above and lookup the callee's address.
To make sure that the existing JIT compilers continue to work
without requiring changes, we keep the imm field as it is.
Signed-off-by: Sandipan Das <redacted>
---
kernel/bpf/verifier.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
@@ -5383,11 +5383,24 @@ static int jit_subprogs(struct bpf_verifier_env *env)insn->src_reg!=BPF_PSEUDO_CALL)continue;subprog=insn->off;-insn->off=0;insn->imm=(u64(*)(u64,u64,u64,u64,u64))func[subprog]->bpf_func-__bpf_call_base;}++/* we use the aux data to keep a list of the start addresses+*oftheJITedimagesforeachfunctionintheprogram+*+*forsomearchitectures,suchaspowerpc64,theimmfield+*mightnotbelargeenoughtoholdtheoffsetofthestart+*addressofthecallee'sJITedimagefrom__bpf_call_base+*+*insuchcases,wecanlookupthestartaddressofacallee+*byusingitssubprogid,availablefromtheofffieldof+*thecallinstruction,asanindexforthislist+*/+func[i]->aux->func=func;+func[i]->aux->func_cnt=env->subprog_cnt+1;
The target tree you have here is infact bpf, since in bpf-next there was a
cleanup where the + 1 is removed. Just for the record that we need to keep
this in mind for bpf into bpf-next merge since this would otherwise subtly
break.
}
for (i = 0; i < env->subprog_cnt; i++) {
old_bpf_func = func[i]->bpf_func;
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-05-18 15:43:39
On 05/18/2018 02:50 PM, Sandipan Das wrote:
quoted hunk
This adds new two new fields to struct bpf_prog_info. For
multi-function programs, these fields can be used to pass
a list of kernel symbol addresses for all functions in a
given program and to userspace using the bpf system call
with the BPF_OBJ_GET_INFO_BY_FD command.
When bpf_jit_kallsyms is enabled, we can get the address
of the corresponding kernel symbol for a callee function
and resolve the symbol's name. The address is determined
by adding the value of the call instruction's imm field
to __bpf_call_base. This offset gets assigned to the imm
field by the verifier.
For some architectures, such as powerpc64, the imm field
is not large enough to hold this offset.
We resolve this by:
[1] Assigning the subprog id to the imm field of a call
instruction in the verifier instead of the offset of
the callee's symbol's address from __bpf_call_base.
[2] Determining the address of a callee's corresponding
symbol by using the imm field as an index for the
list of kernel symbol addresses now available from
the program info.
Suggested-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Sandipan Das <redacted>
---
include/uapi/linux/bpf.h | 2 ++
kernel/bpf/syscall.c | 20 ++++++++++++++++++++
kernel/bpf/verifier.c | 7 +------
3 files changed, 23 insertions(+), 6 deletions(-)
@@ -1933,6 +1933,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,if(!capable(CAP_SYS_ADMIN)){info.jited_prog_len=0;info.xlated_prog_len=0;+info.nr_jited_ksyms=0;gotodone;}
@@ -1981,6 +1982,25 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,}}+ulen=info.nr_jited_ksyms;+info.nr_jited_ksyms=prog->aux->func_cnt;+if(info.nr_jited_ksyms&&ulen){
Since this exposes addresses (though masked one which is correct), this
definitely needs to be guarded with bpf_dump_raw_ok() like we do in other
places here (see JIT dump for example).
quoted hunk
+ u64 __user *user_jited_ksyms = u64_to_user_ptr(info.jited_ksyms);
+ ulong ksym_addr;
+ u32 i;
+
+ /* copy the address of the kernel symbol corresponding to
+ * each function
+ */
+ ulen = min_t(u32, info.nr_jited_ksyms, ulen);
+ for (i = 0; i < ulen; i++) {
+ ksym_addr = (ulong) prog->aux->func[i]->bpf_func;
+ ksym_addr &= PAGE_MASK;
+ if (put_user((u64) ksym_addr, &user_jited_ksyms[i]))
+ return -EFAULT;
+ }
+ }
+
done:
if (copy_to_user(uinfo, &info, info_len) ||
put_user(info_len, &uattr->info.info_len))
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-05-18 15:51:55
On 05/18/2018 02:50 PM, Sandipan Das wrote:
Currently, for multi-function programs, we cannot get the JITed
instructions using the bpf system call's BPF_OBJ_GET_INFO_BY_FD
command. Because of this, userspace tools such as bpftool fail
to identify a multi-function program as being JITed or not.
With the JIT enabled and the test program running, this can be
verified as follows:
# cat /proc/sys/net/core/bpf_jit_enable
1
Before applying this patch:
# bpftool prog list
1: kprobe name foo tag b811aab41a39ad3d gpl
loaded_at 2018-05-16T11:43:38+0530 uid 0
xlated 216B not jited memlock 65536B
...
# bpftool prog dump jited id 1
no instructions returned
After applying this patch:
# bpftool prog list
1: kprobe name foo tag b811aab41a39ad3d gpl
loaded_at 2018-05-16T12:13:01+0530 uid 0
xlated 216B jited 308B memlock 65536B
...
@@ -1896,7 +1896,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,structbpf_prog_infoinfo={};u32info_len=attr->info.info_len;char__user*uinsns;-u32ulen;+u32ulen,i;interr;err=check_uarg_tail_zero(uinfo,sizeof(info),info_len);
@@ -1922,7 +1922,6 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,ulen=min_t(u32,info.nr_map_ids,ulen);if(ulen){u32__user*user_map_ids=u64_to_user_ptr(info.map_ids);-u32i;for(i=0;i<ulen;i++)if(put_user(prog->aux->used_maps[i]->id,
@@ -1970,13 +1969,41 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,*foroffload.*/ulen=info.jited_prog_len;-info.jited_prog_len=prog->jited_len;+if(prog->aux->func_cnt){+info.jited_prog_len=0;+for(i=0;i<prog->aux->func_cnt;i++)+info.jited_prog_len+=prog->aux->func[i]->jited_len;+}else{+info.jited_prog_len=prog->jited_len;+}+if(info.jited_prog_len&&ulen){if(bpf_dump_raw_ok()){uinsns=u64_to_user_ptr(info.jited_prog_insns);ulen=min_t(u32,info.jited_prog_len,ulen);-if(copy_to_user(uinsns,prog->bpf_func,ulen))-return-EFAULT;++/* for multi-function programs, copy the JITed+*instructionsforallthefunctions+*/+if(prog->aux->func_cnt){+u32len,free;+u8*img;++free=ulen;+for(i=0;i<prog->aux->func_cnt;i++){+len=prog->aux->func[i]->jited_len;+img=(u8*)prog->aux->func[i]->bpf_func;+if(len>free)+break;+if(copy_to_user(uinsns,img,len))+return-EFAULT;+uinsns+=len;+free-=len;
Is there any way we can introduce a delimiter between the different
images such that they could be more easily correlated with the call
from the main (or other sub-)program instead of having one contiguous
dump blob?
@@ -1987,7 +2014,6 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, if (info.nr_jited_ksyms && ulen) { u64 __user *user_jited_ksyms = u64_to_user_ptr(info.jited_ksyms); ulong ksym_addr;- u32 i; /* copy the address of the kernel symbol corresponding to * each function
From: Naveen N. Rao <hidden> Date: 2018-05-18 16:05:28
Daniel Borkmann wrote:
On 05/18/2018 02:50 PM, Sandipan Das wrote:
quoted
This adds support for bpf-to-bpf function calls in the powerpc64
JIT compiler. The JIT compiler converts the bpf call instructions
to native branch instructions. After a round of the usual passes,
the start addresses of the JITed images for the callee functions
are known. Finally, to fixup the branch target addresses, we need
to perform an extra pass.
=20
Because of the address range in which JITed images are allocated
on powerpc64, the offsets of the start addresses of these images
from __bpf_call_base are as large as 64 bits. So, for a function
call, we cannot use the imm field of the instruction to determine
the callee's address. Instead, we use the alternative method of
getting it from the list of function addresses in the auxillary
data of the caller by using the off field as an index.
=20
Signed-off-by: Sandipan Das <redacted>
---
arch/powerpc/net/bpf_jit_comp64.c | 79 ++++++++++++++++++++++++++++++++=
/* Assemble the body code between the prologue & epilogue */
static int bpf_jit_build_body(struct bpf_prog *fp, u32 *image,
struct codegen_context *ctx,
- u32 *addrs)
+ u32 *addrs, bool extra_pass)
{
const struct bpf_insn *insn =3D fp->insnsi;
int flen =3D fp->len;
@@ -712,11 +712,23 @@ static int bpf_jit_build_body(struct bpf_prog *fp,=
u32 *image,
quoted
break;
=20
/*
- * Call kernel helper
+ * Call kernel helper or bpf function
*/
case BPF_JMP | BPF_CALL:
ctx->seen |=3D SEEN_FUNC;
- func =3D (u8 *) __bpf_call_base + imm;
+
+ /* bpf function call */
+ if (insn[i].src_reg =3D=3D BPF_PSEUDO_CALL && extra_pass)
=20
Perhaps it might make sense here for !extra_pass to set func to some dumm=
y
address as otherwise the 'kernel helper call' branch used for this is a b=
it
misleading in that sense. The PPC_LI64() used in bpf_jit_emit_func_call()
optimizes the immediate addr, I presume the JIT can handle situations whe=
re
in the final extra_pass the image needs to grow/shrink again (due to diff=
erent
final address for the call)?
That's a good catch. We don't handle that -- we expect to get the size=20
right on first pass. We could probably have PPC_FUNC_ADDR() pad the=20
result with nops to make it a constant 5-instruction sequence.
=20
quoted
+ if (fp->aux->func && off < fp->aux->func_cnt)
+ /* use the subprog id from the off
+ * field to lookup the callee address
+ */
+ func =3D (u8 *) fp->aux->func[off]->bpf_func;
+ else
+ return -EINVAL;
+ /* kernel helper call */
+ else
+ func =3D (u8 *) __bpf_call_base + imm;
=20
bpf_jit_emit_func_call(image, ctx, (u64)func);
=20
@@ -864,6 +876,14 @@ static int bpf_jit_build_body(struct bpf_prog *fp, =
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-05-18 16:09:02
On 05/18/2018 06:05 PM, Naveen N. Rao wrote:
Daniel Borkmann wrote:
quoted
On 05/18/2018 02:50 PM, Sandipan Das wrote:
quoted
This adds support for bpf-to-bpf function calls in the powerpc64
JIT compiler. The JIT compiler converts the bpf call instructions
to native branch instructions. After a round of the usual passes,
the start addresses of the JITed images for the callee functions
are known. Finally, to fixup the branch target addresses, we need
to perform an extra pass.
Because of the address range in which JITed images are allocated
on powerpc64, the offsets of the start addresses of these images
from __bpf_call_base are as large as 64 bits. So, for a function
call, we cannot use the imm field of the instruction to determine
the callee's address. Instead, we use the alternative method of
getting it from the list of function addresses in the auxillary
data of the caller by using the off field as an index.
Signed-off-by: Sandipan Das <redacted>
---
arch/powerpc/net/bpf_jit_comp64.c | 79 ++++++++++++++++++++++++++++++++++-----
1 file changed, 69 insertions(+), 10 deletions(-)
break;
/*
- * Call kernel helper
+ * Call kernel helper or bpf function
*/
case BPF_JMP | BPF_CALL:
ctx->seen |= SEEN_FUNC;
- func = (u8 *) __bpf_call_base + imm;
+
+ /* bpf function call */
+ if (insn[i].src_reg == BPF_PSEUDO_CALL && extra_pass)
Perhaps it might make sense here for !extra_pass to set func to some dummy
address as otherwise the 'kernel helper call' branch used for this is a bit
misleading in that sense. The PPC_LI64() used in bpf_jit_emit_func_call()
optimizes the immediate addr, I presume the JIT can handle situations where
in the final extra_pass the image needs to grow/shrink again (due to different
final address for the call)?
That's a good catch. We don't handle that -- we expect to get the size right on first pass. We could probably have PPC_FUNC_ADDR() pad the result with nops to make it a constant 5-instruction sequence.
Yeah, arm64 does something similar by not optimizing the imm in order to always
emit 4 insns for it.
Thanks,
Daniel
From: Sandipan Das <hidden> Date: 2018-05-18 16:17:25
On 05/18/2018 08:45 PM, Daniel Borkmann wrote:
On 05/18/2018 02:50 PM, Sandipan Das wrote:
quoted
The imm field of a bpf instruction is a signed 32-bit integer.
For JIT bpf-to-bpf function calls, it stores the offset of the
start address of the callee's JITed image from __bpf_call_base.
For some architectures, such as powerpc64, this offset may be
as large as 64 bits and cannot be accomodated in the imm field
without truncation.
We resolve this by:
[1] Additionally using the auxillary data of each function to
keep a list of start addresses of the JITed images for all
functions determined by the verifier.
[2] Retaining the subprog id inside the off field of the call
instructions and using it to index into the list mentioned
above and lookup the callee's address.
To make sure that the existing JIT compilers continue to work
without requiring changes, we keep the imm field as it is.
Signed-off-by: Sandipan Das <redacted>
---
kernel/bpf/verifier.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
@@ -5383,11 +5383,24 @@ static int jit_subprogs(struct bpf_verifier_env *env)insn->src_reg!=BPF_PSEUDO_CALL)continue;subprog=insn->off;-insn->off=0;insn->imm=(u64(*)(u64,u64,u64,u64,u64))func[subprog]->bpf_func-__bpf_call_base;}++/* we use the aux data to keep a list of the start addresses+*oftheJITedimagesforeachfunctionintheprogram+*+*forsomearchitectures,suchaspowerpc64,theimmfield+*mightnotbelargeenoughtoholdtheoffsetofthestart+*addressofthecallee'sJITedimagefrom__bpf_call_base+*+*insuchcases,wecanlookupthestartaddressofacallee+*byusingitssubprogid,availablefromtheofffieldof+*thecallinstruction,asanindexforthislist+*/+func[i]->aux->func=func;+func[i]->aux->func_cnt=env->subprog_cnt+1;
The target tree you have here is infact bpf, since in bpf-next there was a
cleanup where the + 1 is removed. Just for the record that we need to keep
this in mind for bpf into bpf-next merge since this would otherwise subtly
break.
Sorry about the wrong tag. This series is indeed based off bpf-next.
- Sandipan
quoted
}
for (i = 0; i < env->subprog_cnt; i++) {
old_bpf_func = func[i]->bpf_func;
From: Jakub Kicinski <hidden> Date: 2018-05-18 19:55:28
On Fri, 18 May 2018 18:20:38 +0530, Sandipan Das wrote:
Currently, we resolve the callee's address for a JITed function
call by using the imm field of the call instruction as an offset
from __bpf_call_base. If bpf_jit_kallsyms is enabled, we further
use this address to get the callee's kernel symbol's name.
For some architectures, such as powerpc64, the imm field is not
large enough to hold this offset. So, instead of assigning this
offset to the imm field, the verifier now assigns the subprog
id. Also, a list of kernel symbol addresses for all the JITed
functions is provided in the program info. We now use the imm
field as an index for this list to lookup a callee's symbol's
address and resolve its name.
Suggested-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Sandipan Das <redacted>
---
v2:
- Order variables from longest to shortest
- Make sure that ksyms_ptr and ksyms_len are always initialized
- Simplify code
Thanks for the improvements! Since there will be v3 two minor nit
picks still :)
@@ -421,19 +421,26 @@ static int do_show(int argc, char **argv)staticintdo_dump(intargc,char**argv){structbpf_prog_infoinfo={};+unsignedlong*addrs=NULL;structdump_datadd={};__u32len=sizeof(info);unsignedintbuf_size;+unsignedintnr_addrs;char*filepath=NULL;boolopcodes=false;boolvisual=false;unsignedchar*buf;__u32*member_len;__u64*member_ptr;+__u32*ksyms_len;+__u64*ksyms_ptr;ssize_tn;interr;intfd;+ksyms_len=&info.nr_jited_ksyms;+ksyms_ptr=&info.jited_ksyms;
I'm not sure why you need these, why not just access
info.nr_jited_ksyms and info.jited_ksyms directly? "member" variables
are there because jited and xlated images get returned in different
member of struct bpf_prog_info.
quoted hunk
if (is_prefix(*argv, "jited")) {
member_len = &info.jited_prog_len;
member_ptr = &info.jited_prog_insns;
From: Sandipan Das <hidden> Date: 2018-05-21 19:42:28
Hi Daniel,
On 05/18/2018 09:21 PM, Daniel Borkmann wrote:
On 05/18/2018 02:50 PM, Sandipan Das wrote:
quoted
Currently, for multi-function programs, we cannot get the JITed
instructions using the bpf system call's BPF_OBJ_GET_INFO_BY_FD
command. Because of this, userspace tools such as bpftool fail
to identify a multi-function program as being JITed or not.
With the JIT enabled and the test program running, this can be
verified as follows:
# cat /proc/sys/net/core/bpf_jit_enable
1
Before applying this patch:
# bpftool prog list
1: kprobe name foo tag b811aab41a39ad3d gpl
loaded_at 2018-05-16T11:43:38+0530 uid 0
xlated 216B not jited memlock 65536B
...
# bpftool prog dump jited id 1
no instructions returned
After applying this patch:
# bpftool prog list
1: kprobe name foo tag b811aab41a39ad3d gpl
loaded_at 2018-05-16T12:13:01+0530 uid 0
xlated 216B jited 308B memlock 65536B
...
@@ -1896,7 +1896,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,structbpf_prog_infoinfo={};u32info_len=attr->info.info_len;char__user*uinsns;-u32ulen;+u32ulen,i;interr;err=check_uarg_tail_zero(uinfo,sizeof(info),info_len);
@@ -1922,7 +1922,6 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,ulen=min_t(u32,info.nr_map_ids,ulen);if(ulen){u32__user*user_map_ids=u64_to_user_ptr(info.map_ids);-u32i;for(i=0;i<ulen;i++)if(put_user(prog->aux->used_maps[i]->id,
@@ -1970,13 +1969,41 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,*foroffload.*/ulen=info.jited_prog_len;-info.jited_prog_len=prog->jited_len;+if(prog->aux->func_cnt){+info.jited_prog_len=0;+for(i=0;i<prog->aux->func_cnt;i++)+info.jited_prog_len+=prog->aux->func[i]->jited_len;+}else{+info.jited_prog_len=prog->jited_len;+}+if(info.jited_prog_len&&ulen){if(bpf_dump_raw_ok()){uinsns=u64_to_user_ptr(info.jited_prog_insns);ulen=min_t(u32,info.jited_prog_len,ulen);-if(copy_to_user(uinsns,prog->bpf_func,ulen))-return-EFAULT;++/* for multi-function programs, copy the JITed+*instructionsforallthefunctions+*/+if(prog->aux->func_cnt){+u32len,free;+u8*img;++free=ulen;+for(i=0;i<prog->aux->func_cnt;i++){+len=prog->aux->func[i]->jited_len;+img=(u8*)prog->aux->func[i]->bpf_func;+if(len>free)+break;+if(copy_to_user(uinsns,img,len))+return-EFAULT;+uinsns+=len;+free-=len;
Is there any way we can introduce a delimiter between the different
images such that they could be more easily correlated with the call
from the main (or other sub-)program instead of having one contiguous
dump blob?
Can we have another member in bpf_prog_info that points to a list of the lengths of the
JITed images for each subprogram? We can use this information to split up the dump.
- Sandipan
@@ -1987,7 +2014,6 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, if (info.nr_jited_ksyms && ulen) { u64 __user *user_jited_ksyms = u64_to_user_ptr(info.jited_ksyms); ulong ksym_addr;- u32 i; /* copy the address of the kernel symbol corresponding to * each function
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2018-05-22 08:54:37
On 05/21/2018 09:42 PM, Sandipan Das wrote:
On 05/18/2018 09:21 PM, Daniel Borkmann wrote:
quoted
On 05/18/2018 02:50 PM, Sandipan Das wrote:
quoted
Currently, for multi-function programs, we cannot get the JITed
instructions using the bpf system call's BPF_OBJ_GET_INFO_BY_FD
command. Because of this, userspace tools such as bpftool fail
to identify a multi-function program as being JITed or not.
With the JIT enabled and the test program running, this can be
verified as follows:
# cat /proc/sys/net/core/bpf_jit_enable
1
Before applying this patch:
# bpftool prog list
1: kprobe name foo tag b811aab41a39ad3d gpl
loaded_at 2018-05-16T11:43:38+0530 uid 0
xlated 216B not jited memlock 65536B
...
# bpftool prog dump jited id 1
no instructions returned
After applying this patch:
# bpftool prog list
1: kprobe name foo tag b811aab41a39ad3d gpl
loaded_at 2018-05-16T12:13:01+0530 uid 0
xlated 216B jited 308B memlock 65536B
...
@@ -1896,7 +1896,7 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,structbpf_prog_infoinfo={};u32info_len=attr->info.info_len;char__user*uinsns;-u32ulen;+u32ulen,i;interr;err=check_uarg_tail_zero(uinfo,sizeof(info),info_len);
@@ -1922,7 +1922,6 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,ulen=min_t(u32,info.nr_map_ids,ulen);if(ulen){u32__user*user_map_ids=u64_to_user_ptr(info.map_ids);-u32i;for(i=0;i<ulen;i++)if(put_user(prog->aux->used_maps[i]->id,
@@ -1970,13 +1969,41 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,*foroffload.*/ulen=info.jited_prog_len;-info.jited_prog_len=prog->jited_len;+if(prog->aux->func_cnt){+info.jited_prog_len=0;+for(i=0;i<prog->aux->func_cnt;i++)+info.jited_prog_len+=prog->aux->func[i]->jited_len;+}else{+info.jited_prog_len=prog->jited_len;+}+if(info.jited_prog_len&&ulen){if(bpf_dump_raw_ok()){uinsns=u64_to_user_ptr(info.jited_prog_insns);ulen=min_t(u32,info.jited_prog_len,ulen);-if(copy_to_user(uinsns,prog->bpf_func,ulen))-return-EFAULT;++/* for multi-function programs, copy the JITed+*instructionsforallthefunctions+*/+if(prog->aux->func_cnt){+u32len,free;+u8*img;++free=ulen;+for(i=0;i<prog->aux->func_cnt;i++){+len=prog->aux->func[i]->jited_len;+img=(u8*)prog->aux->func[i]->bpf_func;+if(len>free)+break;+if(copy_to_user(uinsns,img,len))+return-EFAULT;+uinsns+=len;+free-=len;
Is there any way we can introduce a delimiter between the different
images such that they could be more easily correlated with the call
from the main (or other sub-)program instead of having one contiguous
dump blob?
Can we have another member in bpf_prog_info that points to a list of the lengths of the
JITed images for each subprogram? We can use this information to split up the dump.