From patchwork Tue Mar 16 01:13:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin KaFai Lau X-Patchwork-Id: 12140901 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-19.0 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS, URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9F581C433E0 for ; Tue, 16 Mar 2021 01:14:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 671B264F6D for ; Tue, 16 Mar 2021 01:14:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234162AbhCPBOY (ORCPT ); Mon, 15 Mar 2021 21:14:24 -0400 Received: from mx0a-00082601.pphosted.com ([67.231.145.42]:55372 "EHLO mx0a-00082601.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234143AbhCPBN5 (ORCPT ); Mon, 15 Mar 2021 21:13:57 -0400 Received: from pps.filterd (m0044012.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.16.0.43/8.16.0.43) with SMTP id 12G1AoY5016477 for ; Mon, 15 Mar 2021 18:13:57 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fb.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : content-type : content-transfer-encoding : mime-version; s=facebook; bh=xwwBwHqzy8q8MyRsfYvaHjpgtgOnPHWDwYSpwxXwFIs=; b=ePuyEhxnKFZgYEaytHAJ7UNJvspoyhbNEf593TiahX/1h1QqISgNi/hTenfx8CYA6I2v QDWUI8j1oQ79Uz/asNqPcbH9RZ5ZUmJYBTSsJp25U28tCFMhwOKATia/fiDxiehD3MsM SlECihwBd3rFtc/MZE5QfRZzgkd5mf7NVQI= Received: from maileast.thefacebook.com ([163.114.130.16]) by mx0a-00082601.pphosted.com with ESMTP id 379e3ursrf-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Mon, 15 Mar 2021 18:13:57 -0700 Received: from intmgw001.37.frc1.facebook.com (2620:10d:c0a8:1b::d) by mail.thefacebook.com (2620:10d:c0a8:83::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.2; Mon, 15 Mar 2021 18:13:55 -0700 Received: by devbig005.ftw2.facebook.com (Postfix, from userid 6611) id D23CA2942B4F; Mon, 15 Mar 2021 18:13:48 -0700 (PDT) From: Martin KaFai Lau To: CC: Alexei Starovoitov , Daniel Borkmann , , Subject: [PATCH bpf-next 02/15] bpf: btf: Support parsing extern func Date: Mon, 15 Mar 2021 18:13:48 -0700 Message-ID: <20210316011348.4175708-1-kafai@fb.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210316011336.4173585-1-kafai@fb.com> References: <20210316011336.4173585-1-kafai@fb.com> X-FB-Internal: Safe X-Proofpoint-UnRewURL: 0 URL was un-rewritten MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.369,18.0.761 definitions=2021-03-15_15:2021-03-15,2021-03-15 signatures=0 X-Proofpoint-Spam-Details: rule=fb_default_notspam policy=fb_default score=0 malwarescore=0 bulkscore=0 phishscore=0 adultscore=0 suspectscore=0 impostorscore=0 priorityscore=1501 clxscore=1015 lowpriorityscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2103160005 X-FB-Internal: deliver Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net This patch makes BTF verifier to accept extern func. It is used for allowing bpf program to call a limited set of kernel functions in a later patch. When writing bpf prog, the extern kernel function needs to be declared under a ELF section (".ksyms") which is the same as the current extern kernel variables and that should keep its usage consistent without requiring to remember another section name. For example, in a bpf_prog.c: extern int foo(struct sock *) __attribute__((section(".ksyms"))) [24] FUNC_PROTO '(anon)' ret_type_id=15 vlen=1 '(anon)' type_id=18 [25] FUNC 'foo' type_id=24 linkage=extern [ ... ] [33] DATASEC '.ksyms' size=0 vlen=1 type_id=25 offset=0 size=0 LLVM will put the "func" type into the BTF datasec ".ksyms". The current "btf_datasec_check_meta()" assumes everything under it is a "var" and ensures it has non-zero size ("!vsi->size" test). The non-zero size check is not true for "func". This patch postpones the "!vsi-size" test from "btf_datasec_check_meta()" to "btf_datasec_resolve()" which has all types collected to decide if a vsi is a "var" or a "func" and then enforce the "vsi->size" differently. If the datasec only has "func", its "t->size" could be zero. Thus, the current "!t->size" test is no longer valid. The invalid "t->size" will still be caught by the later "last_vsi_end_off > t->size" check. This patch also takes this chance to consolidate other "t->size" tests ("vsi->offset >= t->size" "vsi->size > t->size", and "t->size < sum") into the existing "last_vsi_end_off > t->size" test. The LLVM will also put those extern kernel function as an extern linkage func in the BTF: [24] FUNC_PROTO '(anon)' ret_type_id=15 vlen=1 '(anon)' type_id=18 [25] FUNC 'foo' type_id=24 linkage=extern This patch allows BTF_FUNC_EXTERN in btf_func_check_meta(). Also extern kernel function declaration does not necessary have arg name. Another change in btf_func_check() is to allow extern function having no arg name. The btf selftest is adjusted accordingly. New tests are also added. The required LLVM patch: https://reviews.llvm.org/D93563 Signed-off-by: Martin KaFai Lau --- kernel/bpf/btf.c | 52 ++++--- tools/testing/selftests/bpf/prog_tests/btf.c | 154 ++++++++++++++++++- 2 files changed, 178 insertions(+), 28 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 369faeddf1df..96cd24020a38 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3439,7 +3439,7 @@ static s32 btf_func_check_meta(struct btf_verifier_env *env, return -EINVAL; } - if (btf_type_vlen(t) > BTF_FUNC_GLOBAL) { + if (btf_type_vlen(t) > BTF_FUNC_EXTERN) { btf_verifier_log_type(env, t, "Invalid func linkage"); return -EINVAL; } @@ -3532,7 +3532,7 @@ static s32 btf_datasec_check_meta(struct btf_verifier_env *env, u32 meta_left) { const struct btf_var_secinfo *vsi; - u64 last_vsi_end_off = 0, sum = 0; + u64 last_vsi_end_off = 0; u32 i, meta_needed; meta_needed = btf_type_vlen(t) * sizeof(*vsi); @@ -3543,11 +3543,6 @@ static s32 btf_datasec_check_meta(struct btf_verifier_env *env, return -EINVAL; } - if (!t->size) { - btf_verifier_log_type(env, t, "size == 0"); - return -EINVAL; - } - if (btf_type_kflag(t)) { btf_verifier_log_type(env, t, "Invalid btf_info kind_flag"); return -EINVAL; @@ -3569,19 +3564,13 @@ static s32 btf_datasec_check_meta(struct btf_verifier_env *env, return -EINVAL; } - if (vsi->offset < last_vsi_end_off || vsi->offset >= t->size) { + if (vsi->offset < last_vsi_end_off) { btf_verifier_log_vsi(env, t, vsi, "Invalid offset"); return -EINVAL; } - if (!vsi->size || vsi->size > t->size) { - btf_verifier_log_vsi(env, t, vsi, - "Invalid size"); - return -EINVAL; - } - - last_vsi_end_off = vsi->offset + vsi->size; + last_vsi_end_off = (u64)vsi->offset + vsi->size; if (last_vsi_end_off > t->size) { btf_verifier_log_vsi(env, t, vsi, "Invalid offset+size"); @@ -3589,12 +3578,6 @@ static s32 btf_datasec_check_meta(struct btf_verifier_env *env, } btf_verifier_log_vsi(env, t, vsi, NULL); - sum += vsi->size; - } - - if (t->size < sum) { - btf_verifier_log_type(env, t, "Invalid btf_info size"); - return -EINVAL; } return meta_needed; @@ -3611,9 +3594,28 @@ static int btf_datasec_resolve(struct btf_verifier_env *env, u32 var_type_id = vsi->type, type_id, type_size = 0; const struct btf_type *var_type = btf_type_by_id(env->btf, var_type_id); - if (!var_type || !btf_type_is_var(var_type)) { + if (!var_type) { + btf_verifier_log_vsi(env, v->t, vsi, + "type not found"); + return -EINVAL; + } + + if (btf_type_is_func(var_type)) { + if (vsi->size || vsi->offset) { + btf_verifier_log_vsi(env, v->t, vsi, + "Invalid size/offset"); + return -EINVAL; + } + continue; + } else if (btf_type_is_var(var_type)) { + if (!vsi->size) { + btf_verifier_log_vsi(env, v->t, vsi, + "Invalid size"); + return -EINVAL; + } + } else { btf_verifier_log_vsi(env, v->t, vsi, - "Not a VAR kind member"); + "Neither a VAR nor a FUNC"); return -EINVAL; } @@ -3849,9 +3851,11 @@ static int btf_func_check(struct btf_verifier_env *env, const struct btf_param *args; const struct btf *btf; u16 nr_args, i; + bool is_extern; btf = env->btf; proto_type = btf_type_by_id(btf, t->type); + is_extern = btf_type_vlen(t) == BTF_FUNC_EXTERN; if (!proto_type || !btf_type_is_func_proto(proto_type)) { btf_verifier_log_type(env, t, "Invalid type_id"); @@ -3861,7 +3865,7 @@ static int btf_func_check(struct btf_verifier_env *env, args = (const struct btf_param *)(proto_type + 1); nr_args = btf_type_vlen(proto_type); for (i = 0; i < nr_args; i++) { - if (!args[i].name_off && args[i].type) { + if (!is_extern && !args[i].name_off && args[i].type) { btf_verifier_log_type(env, t, "Invalid arg#%u", i + 1); return -EINVAL; } diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c index 0457ae32b270..e469482833b2 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf.c +++ b/tools/testing/selftests/bpf/prog_tests/btf.c @@ -498,7 +498,7 @@ static struct btf_raw_test raw_tests[] = { .value_type_id = 7, .max_entries = 1, .btf_load_err = true, - .err_str = "Invalid size", + .err_str = "Invalid offset+size", }, { .descr = "global data test #10, invalid var size", @@ -696,7 +696,7 @@ static struct btf_raw_test raw_tests[] = { .err_str = "Invalid offset", }, { - .descr = "global data test #15, not var kind", + .descr = "global data test #15, not var/func kind", .raw_types = { /* int */ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ @@ -716,7 +716,7 @@ static struct btf_raw_test raw_tests[] = { .value_type_id = 3, .max_entries = 1, .btf_load_err = true, - .err_str = "Not a VAR kind member", + .err_str = "Neither a VAR nor a FUNC", }, { .descr = "global data test #16, invalid var referencing sec", @@ -2803,7 +2803,7 @@ static struct btf_raw_test raw_tests[] = { BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1), BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 2), /* void func(int a, unsigned int b) */ - BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 2), 3), /* [4] */ + BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 3), 3), /* [4] */ BTF_END_RAW, }, .str_sec = "\0a\0b\0func", @@ -3531,6 +3531,152 @@ static struct btf_raw_test raw_tests[] = { .max_entries = 1, }, +{ + .descr = "datasec: func only", + .raw_types = { + /* int */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + /* void (*)(void) */ + BTF_FUNC_PROTO_ENC(0, 0), /* [2] */ + BTF_FUNC_ENC(NAME_NTH(1), 2), /* [3] */ + BTF_FUNC_ENC(NAME_NTH(2), 2), /* [4] */ + /* .ksym section */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 2), 0), /* [5] */ + BTF_VAR_SECINFO_ENC(3, 0, 0), + BTF_VAR_SECINFO_ENC(4, 0, 0), + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo1\0foo2\0.ksym\0"), + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 1, +}, + +{ + .descr = "datasec: func and var", + .raw_types = { + /* int */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + /* void (*)(void) */ + BTF_FUNC_PROTO_ENC(0, 0), /* [2] */ + BTF_FUNC_ENC(NAME_NTH(1), 2), /* [3] */ + BTF_FUNC_ENC(NAME_NTH(2), 2), /* [4] */ + /* int */ + BTF_VAR_ENC(NAME_NTH(4), 1, 0), /* [5] */ + BTF_VAR_ENC(NAME_NTH(5), 1, 0), /* [6] */ + /* .ksym section */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 4), 8), /* [7] */ + BTF_VAR_SECINFO_ENC(3, 0, 0), + BTF_VAR_SECINFO_ENC(4, 0, 0), + BTF_VAR_SECINFO_ENC(5, 0, 4), + BTF_VAR_SECINFO_ENC(6, 4, 4), + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo1\0foo2\0.ksym\0a\0b\0"), + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 1, +}, + +{ + .descr = "datasec: func and var, invalid size/offset for func", + .raw_types = { + /* int */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + /* void (*)(void) */ + BTF_FUNC_PROTO_ENC(0, 0), /* [2] */ + BTF_FUNC_ENC(NAME_NTH(1), 2), /* [3] */ + BTF_FUNC_ENC(NAME_NTH(2), 2), /* [4] */ + /* int */ + BTF_VAR_ENC(NAME_NTH(4), 1, 0), /* [5] */ + BTF_VAR_ENC(NAME_NTH(5), 1, 0), /* [6] */ + /* .ksym section */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 4), 8), /* [7] */ + BTF_VAR_SECINFO_ENC(3, 0, 0), + BTF_VAR_SECINFO_ENC(5, 0, 4), + BTF_VAR_SECINFO_ENC(4, 4, 0), /* func has non zero vsi->offset */ + BTF_VAR_SECINFO_ENC(6, 4, 4), + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo1\0foo2\0.ksym\0a\0b\0"), + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 1, + .btf_load_err = true, + .err_str = "Invalid size/offset", +}, + +{ + .descr = "datasec: func and var, datasec size 0", + .raw_types = { + /* int */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + /* void (*)(void) */ + BTF_FUNC_PROTO_ENC(0, 0), /* [2] */ + BTF_FUNC_ENC(NAME_NTH(1), 2), /* [3] */ + BTF_FUNC_ENC(NAME_NTH(2), 2), /* [4] */ + /* int */ + BTF_VAR_ENC(NAME_NTH(4), 1, 0), /* [5] */ + BTF_VAR_ENC(NAME_NTH(5), 1, 0), /* [6] */ + /* .ksym section */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 4), 0), /* [7] */ + BTF_VAR_SECINFO_ENC(3, 0, 0), + BTF_VAR_SECINFO_ENC(4, 0, 0), + BTF_VAR_SECINFO_ENC(5, 0, 4), + BTF_VAR_SECINFO_ENC(6, 4, 4), + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo1\0foo2\0.ksym\0a\0b\0"), + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 1, + .btf_load_err = true, + .err_str = "Invalid offset+size", +}, + +{ + .descr = "datasec: func and var, zero vsi->size for var", + .raw_types = { + /* int */ + BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ + /* void (*)(void) */ + BTF_FUNC_PROTO_ENC(0, 0), /* [2] */ + BTF_FUNC_ENC(NAME_NTH(1), 2), /* [3] */ + BTF_FUNC_ENC(NAME_NTH(2), 2), /* [4] */ + /* int */ + BTF_VAR_ENC(NAME_NTH(4), 1, 0), /* [5] */ + BTF_VAR_ENC(NAME_NTH(5), 1, 0), /* [6] */ + /* .ksym section */ + BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 4), 8), /* [7] */ + BTF_VAR_SECINFO_ENC(3, 0, 0), + BTF_VAR_SECINFO_ENC(4, 0, 0), + BTF_VAR_SECINFO_ENC(5, 0, 0), /* var has zero vsi->size */ + BTF_VAR_SECINFO_ENC(6, 0, 4), + BTF_END_RAW, + }, + BTF_STR_SEC("\0foo1\0foo2\0.ksym\0a\0b\0"), + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = sizeof(int), + .key_type_id = 1, + .value_type_id = 1, + .max_entries = 1, + .btf_load_err = true, + .err_str = "Invalid size", +}, + { .descr = "float test #1, well-formed", .raw_types = {