btf_parse_module() makes its own kvmemdup() of the module's .BTF
section, and of its .BTF.inline section through btf_parse_split().
Move both copies to the caller, btf_module_notify(), and let the result
take ownership of them: the new struct btf the module BTF copy, and the
returned inline BTF the inline copy, relocated in place.  On failure,
and for an inline BTF that fails validation, the caller frees what was
not taken.

No functional change: /sys/kernel/btf/<module> and <module>.inline keep
serving btf->data and the relocated inline BTF, which are the same
buffers as before.

This prepares for parsing the vmlinux BTF on first use (later in this
series): a module that loads before that keeps its copies unparsed and
exposes them in sysfs right away.  Parsing them later must adopt those
buffers rather than make second copies, so that the sysfs files stay
valid and the data exists only once.

Co-developed-by: Ihor Solodrai <[email protected]>
TODO-NEEDS-IHOR: Signed-off-by: Ihor Solodrai <[email protected]>
Signed-off-by: Jay Wang <[email protected]>
---
 kernel/bpf/btf.c | 47 ++++++++++++++++++++++++++++++++++-------------
 1 file changed, 34 insertions(+), 13 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 58d8b21d9895..59d6f44cbe17 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -6969,8 +6969,9 @@ __u32 btf_relocate_id(const struct btf *btf, __u32 id)
 
 #ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 
+/* @data is a copy owned by the returned BTF; the caller frees it on error */
 static struct btf *btf_parse_split(struct btf_verifier_env *env,
-                                  const char *name, const void *data,
+                                  const char *name, void *data,
                                   unsigned int data_size, struct btf *base_btf)
 {
        struct btf *btf;
@@ -6986,11 +6987,7 @@ static struct btf *btf_parse_split(struct 
btf_verifier_env *env,
        btf->named_start_id = 0;
        strscpy(btf->name, name);
 
-       btf->data = kvmemdup(data, data_size, GFP_KERNEL | __GFP_NOWARN);
-       if (!btf->data) {
-               err = -ENOMEM;
-               goto errout;
-       }
+       btf->data = data;
        btf->data_size = data_size;
 
        err = btf_parse_hdr(env);
@@ -7014,6 +7011,7 @@ static struct btf *btf_parse_split(struct 
btf_verifier_env *env,
        return btf;
 
 errout:
+       btf->data = NULL;
        btf_free(btf);
        return ERR_PTR(err);
 }
@@ -7085,10 +7083,16 @@ static int btf_rebase_inline(struct btf *inline_btf,
        return 0;
 }
 
-static struct btf *btf_parse_module(const char *module_name, const void *data,
+/*
+ * @data and @inline_data are copies made by the caller.  On success the
+ * returned BTF owns @data, and *@relocated_inline_data is @inline_data,
+ * relocated in place, unless the inline BTF is invalid.  The caller frees
+ * the copies this does not take.
+ */
+static struct btf *btf_parse_module(const char *module_name, void *data,
                                    unsigned int data_size, void *base_data,
                                    unsigned int base_data_size,
-                                   const void *inline_data,
+                                   void *inline_data,
                                    unsigned int inline_data_size,
                                    void **relocated_inline_data)
 {
@@ -7156,6 +7160,7 @@ static struct btf *btf_parse_module(const char 
*module_name, const void *data,
                if (err) {
                        pr_warn("failed to relocate module [%s] inline BTF: 
%d\n",
                                module_name, err);
+                       inline_btf->data = NULL;
                        btf_free(inline_btf);
                } else {
                        *relocated_inline_data = inline_btf->data;
@@ -7179,12 +7184,16 @@ static struct btf *btf_parse_module(const char 
*module_name, const void *data,
 errout:
        kvfree(module_str_map);
        btf_verifier_env_free(env);
-       if (inline_btf)
+       if (inline_btf) {
+               inline_btf->data = NULL;
                btf_free(inline_btf);
+       }
        if (!IS_ERR(base_btf) && base_btf != vmlinux_btf)
                btf_free(base_btf);
-       if (btf)
+       if (btf) {
+               btf->data = NULL;
                btf_free(btf);
+       }
        return ERR_PTR(err);
 }
 
@@ -9445,7 +9454,7 @@ static int btf_module_notify(struct notifier_block *nb, 
unsigned long op,
        struct module *mod = module;
        struct bin_attribute *attr;
        struct btf *btf;
-       void *inline_data = NULL, *relocated_inline_data = NULL;
+       void *data, *inline_data = NULL, *relocated_inline_data = NULL;
        unsigned int inline_data_size = 0;
        int err = 0;
 
@@ -9473,11 +9482,23 @@ static int btf_module_notify(struct notifier_block *nb, 
unsigned long op,
                        err = -ENOMEM;
                        goto out;
                }
-               btf = btf_parse_module(mod->name, mod->btf_data, 
mod->btf_data_size,
+               data = kvmemdup(mod->btf_data, mod->btf_data_size, GFP_KERNEL | 
__GFP_NOWARN);
+               if (inline_data) {
+                       inline_data = kvmemdup(inline_data, inline_data_size,
+                                              GFP_KERNEL | __GFP_NOWARN);
+                       if (!inline_data)
+                               pr_warn("failed to validate module [%s] inline 
BTF: %d\n",
+                                       mod->name, -ENOMEM);
+               }
+               btf = !data ? ERR_PTR(-ENOMEM) :
+                     btf_parse_module(mod->name, data, mod->btf_data_size,
                                       mod->btf_base_data, 
mod->btf_base_data_size,
-                                      inline_data, inline_data_size,
+                                      inline_data, inline_data ? 
inline_data_size : 0,
                                       &relocated_inline_data);
+               if (inline_data != relocated_inline_data)
+                       kvfree(inline_data);
                if (IS_ERR(btf)) {
+                       kvfree(data);
                        kfree(btf_mod);
                        if (!IS_ENABLED(CONFIG_MODULE_ALLOW_BTF_MISMATCH)) {
                                pr_warn("failed to validate module [%s] BTF: 
%ld\n",
-- 
2.47.3


Reply via email to