Re: [PATCH v4 1/4] of: change overlay apply input data from unflattened to FDT

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

 



On Thu, Mar 1, 2018 at 12:00 PM,  <frowand.list@xxxxxxxxx> wrote:
> From: Frank Rowand <frank.rowand@xxxxxxxx>
>
> Move duplicating and unflattening of an overlay flattened devicetree
> (FDT) into the overlay application code.  To accomplish this,
> of_overlay_apply() is replaced by of_overlay_fdt_apply().
>
> The copy of the FDT (aka "duplicate FDT") now belongs to devicetree
> code, which is thus responsible for freeing the duplicate FDT.  The
> caller of of_overlay_fdt_apply() remains responsible for freeing the
> original FDT.
>
> The unflattened devicetree now belongs to devicetree code, which is
> thus responsible for freeing the unflattened devicetree.
>
> These ownership changes prevent early freeing of the duplicated FDT
> or the unflattened devicetree, which could result in use after free
> errors.
>
> of_overlay_fdt_apply() is a private function for the anticipated
> overlay loader.
>
> Update unittest.c to use of_overlay_fdt_apply() instead of
> of_overlay_apply().
>
> Move overlay fragments from artificial locations in
> drivers/of/unittest-data/tests-overlay.dtsi into one devicetree
> source file per overlay.  This led to changes in
> drivers/of/unitest-data/Makefile and drivers/of/unitest.c.
>
>   - Add overlay directives to the overlay devicetree source files so
>     that dtc will compile them as true overlays into one FDT data
>     chunk per overlay.
>
>   - Set CFLAGS for drivers/of/unittest-data/testcases.dts so that
>     symbols will be generated for overlay resolution of overlays
>     that are no longer artificially contained in testcases.dts
>
>   - Unflatten and apply each unittest overlay FDT using
>     of_overlay_fdt_apply().
>
>   - Enable the of_resolve_phandles() check for whether the unflattened
>     overlay is detached.  This check was previously disabled because the
>     overlays from tests-overlay.dtsi were not unflattened into detached
>     trees.
>
>   - Other changes to unittest.c infrastructure to manage multiple test
>     FDTs built into the kernel image (access by name instead of
>     arbitrary number).
>
>   - of_unittest_overlay_high_level(): previously unused code to add
>     properties from the overlay_base devicetree to the live tree
>     was triggered by the restructuring of tests-overlay.dtsi and thus
>     testcases.dts.  This exposed two bugs: (1) the need to dup a
>     property before adding it, and (2) property 'name' is
>     auto-generated in the unflatten code and thus will be a duplicate
>     in the __symbols__ node - do not treat this duplicate as an error.
>
> Signed-off-by: Frank Rowand <frank.rowand@xxxxxxxx>
> ---
>
> There are checkpatch warnings.  I have reviewed them and feel they
> can be ignored.  They are "line over 80 characters" for either
> pre-existing long lines, or lines in devicetree source files.
>
> Changes from v3:
>   - OF_OVERLAY: add select OF_FLATTREE
>
> Changes from v1:
>   - rebase on v4.16-rc1
>
>  drivers/of/Kconfig                          |   1 +
>  drivers/of/of_private.h                     |   1 +
>  drivers/of/overlay.c                        | 107 +++++++++-
>  drivers/of/resolver.c                       |   6 -
>  drivers/of/unittest-data/Makefile           |  28 ++-
>  drivers/of/unittest-data/overlay_0.dts      |  14 ++
>  drivers/of/unittest-data/overlay_1.dts      |  14 ++
>  drivers/of/unittest-data/overlay_10.dts     |  34 ++++
>  drivers/of/unittest-data/overlay_11.dts     |  34 ++++
>  drivers/of/unittest-data/overlay_12.dts     |  14 ++
>  drivers/of/unittest-data/overlay_13.dts     |  14 ++
>  drivers/of/unittest-data/overlay_15.dts     |  35 ++++
>  drivers/of/unittest-data/overlay_2.dts      |  14 ++
>  drivers/of/unittest-data/overlay_3.dts      |  14 ++
>  drivers/of/unittest-data/overlay_4.dts      |  23 +++
>  drivers/of/unittest-data/overlay_5.dts      |  14 ++
>  drivers/of/unittest-data/overlay_6.dts      |  15 ++
>  drivers/of/unittest-data/overlay_7.dts      |  15 ++
>  drivers/of/unittest-data/overlay_8.dts      |  15 ++
>  drivers/of/unittest-data/overlay_9.dts      |  15 ++
>  drivers/of/unittest-data/tests-overlay.dtsi | 213 --------------------
>  drivers/of/unittest.c                       | 294 ++++++++++++++--------------
>  include/linux/of.h                          |   7 -
>  23 files changed, 552 insertions(+), 389 deletions(-)
>  create mode 100644 drivers/of/unittest-data/overlay_0.dts
>  create mode 100644 drivers/of/unittest-data/overlay_1.dts
>  create mode 100644 drivers/of/unittest-data/overlay_10.dts
>  create mode 100644 drivers/of/unittest-data/overlay_11.dts
>  create mode 100644 drivers/of/unittest-data/overlay_12.dts
>  create mode 100644 drivers/of/unittest-data/overlay_13.dts
>  create mode 100644 drivers/of/unittest-data/overlay_15.dts
>  create mode 100644 drivers/of/unittest-data/overlay_2.dts
>  create mode 100644 drivers/of/unittest-data/overlay_3.dts
>  create mode 100644 drivers/of/unittest-data/overlay_4.dts
>  create mode 100644 drivers/of/unittest-data/overlay_5.dts
>  create mode 100644 drivers/of/unittest-data/overlay_6.dts
>  create mode 100644 drivers/of/unittest-data/overlay_7.dts
>  create mode 100644 drivers/of/unittest-data/overlay_8.dts
>  create mode 100644 drivers/of/unittest-data/overlay_9.dts
>
> diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
> index 783e0870bd22..ad3fcad4d75b 100644
> --- a/drivers/of/Kconfig
> +++ b/drivers/of/Kconfig
> @@ -92,6 +92,7 @@ config OF_RESOLVE
>  config OF_OVERLAY
>         bool "Device Tree overlays"
>         select OF_DYNAMIC
> +       select OF_FLATTREE
>         select OF_RESOLVE
>         help
>           Overlays are a method to dynamically modify part of the kernel's
> diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
> index 0c609e7d0334..6e39dce3a979 100644
> --- a/drivers/of/of_private.h
> +++ b/drivers/of/of_private.h
> @@ -77,6 +77,7 @@ static inline void __of_detach_node_sysfs(struct device_node *np) {}
>  #endif
>
>  #if defined(CONFIG_OF_OVERLAY)
> +int of_overlay_fdt_apply(void *overlay_fdt, int *ovcs_id);
>  void of_overlay_mutex_lock(void);
>  void of_overlay_mutex_unlock(void);
>  #else
> diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
> index 3397d7642958..5f6c5569e97d 100644
> --- a/drivers/of/overlay.c
> +++ b/drivers/of/overlay.c
> @@ -12,10 +12,12 @@
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_device.h>
> +#include <linux/of_fdt.h>
>  #include <linux/string.h>
>  #include <linux/ctype.h>
>  #include <linux/errno.h>
>  #include <linux/slab.h>
> +#include <linux/libfdt.h>
>  #include <linux/err.h>
>  #include <linux/idr.h>
>
> @@ -33,7 +35,9 @@ struct fragment {
>
>  /**
>   * struct overlay_changeset
> + * @id:                        changeset identifier
>   * @ovcs_list:         list on which we are located
> + * @fdt:               FDT that was unflattened to create @overlay_tree
>   * @overlay_tree:      expanded device tree that contains the fragment nodes
>   * @count:             count of fragment structures
>   * @fragments:         fragment nodes in the overlay expanded device tree
> @@ -43,6 +47,7 @@ struct fragment {
>  struct overlay_changeset {
>         int id;
>         struct list_head ovcs_list;
> +       const void *fdt;
>         struct device_node *overlay_tree;
>         int count;
>         struct fragment *fragments;
> @@ -503,7 +508,8 @@ static struct device_node *find_target_node(struct device_node *info_node)
>
>  /**
>   * init_overlay_changeset() - initialize overlay changeset from overlay tree
> - * @ovcs       Overlay changeset to build
> + * @ovcs:      Overlay changeset to build
> + * @fdt:       the FDT that was unflattened to create @tree
>   * @tree:      Contains all the overlay fragments and overlay fixup nodes
>   *
>   * Initialize @ovcs.  Populate @ovcs->fragments with node information from
> @@ -514,7 +520,7 @@ static struct device_node *find_target_node(struct device_node *info_node)
>   * detected in @tree, or -ENOSPC if idr_alloc() error.
>   */
>  static int init_overlay_changeset(struct overlay_changeset *ovcs,
> -               struct device_node *tree)
> +               const void *fdt, struct device_node *tree)
>  {
>         struct device_node *node, *overlay_node;
>         struct fragment *fragment;
> @@ -535,6 +541,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
>                 pr_debug("%s() tree is not root\n", __func__);
>
>         ovcs->overlay_tree = tree;
> +       ovcs->fdt = fdt;
>
>         INIT_LIST_HEAD(&ovcs->ovcs_list);
>
> @@ -606,6 +613,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
>         }
>
>         if (!cnt) {
> +               pr_err("no fragments or symbols in overlay\n");
>                 ret = -EINVAL;
>                 goto err_free_fragments;
>         }
> @@ -642,11 +650,24 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
>         }
>         kfree(ovcs->fragments);
>
> +       /*
> +        * TODO
> +        *
> +        * would like to: kfree(ovcs->overlay_tree);
> +        * but can not since drivers may have pointers into this data
> +        *
> +        * would like to: kfree(ovcs->fdt);
> +        * but can not since drivers may have pointers into this data
> +        */
> +
>         kfree(ovcs);
>  }
>
> -/**
> +/*
> + * internal documentation
> + *
>   * of_overlay_apply() - Create and apply an overlay changeset
> + * @fdt:       the FDT that was unflattened to create @tree
>   * @tree:      Expanded overlay device tree
>   * @ovcs_id:   Pointer to overlay changeset id
>   *
> @@ -685,21 +706,29 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
>   * id is returned to *ovcs_id.
>   */
>
> -int of_overlay_apply(struct device_node *tree, int *ovcs_id)
> +static int of_overlay_apply(const void *fdt, struct device_node *tree,
> +               int *ovcs_id)
>  {
>         struct overlay_changeset *ovcs;
>         int ret = 0, ret_revert, ret_tmp;
>
> -       *ovcs_id = 0;
> +       /*
> +        * As of this point, fdt and tree belong to the overlay changeset.
> +        * overlay changeset code is responsible for freeing them.
> +        */
>
>         if (devicetree_corrupt()) {
>                 pr_err("devicetree state suspect, refuse to apply overlay\n");
> +               kfree(fdt);
> +               kfree(tree);
>                 ret = -EBUSY;
>                 goto out;
>         }
>
>         ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL);
>         if (!ovcs) {
> +               kfree(fdt);

You free the fdt up here, but ...

> +               kfree(tree);
>                 ret = -ENOMEM;
>                 goto out;
>         }
> @@ -709,12 +738,17 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
>
>         ret = of_resolve_phandles(tree);
>         if (ret)
> -               goto err_free_overlay_changeset;
> +               goto err_free_tree;
>
> -       ret = init_overlay_changeset(ovcs, tree);
> +       ret = init_overlay_changeset(ovcs, fdt, tree);
>         if (ret)
> -               goto err_free_overlay_changeset;
> +               goto err_free_tree;
>
> +       /*
> +        * after overlay_notify(), ovcs->overlay_tree related pointers may have
> +        * leaked to drivers, so can not kfree() tree, aka ovcs->overlay_tree;
> +        * and can not free fdt, aka ovcs->fdt
> +        */
>         ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY);
>         if (ret) {
>                 pr_err("overlay changeset pre-apply notify error %d\n", ret);
> @@ -754,6 +788,9 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
>
>         goto out_unlock;
>
> +err_free_tree:
> +       kfree(tree);
> +

...not down here?

Seems like of_overlay_fdt_apply should do the freeing as that is what
does the allocation of the fdt.

>  err_free_overlay_changeset:
>         free_overlay_changeset(ovcs);
>
> @@ -766,7 +803,59 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
>
>         return ret;
>  }
> -EXPORT_SYMBOL_GPL(of_overlay_apply);
> +
> +int of_overlay_fdt_apply(void *overlay_fdt, int *ovcs_id)
> +{
> +       const void *new_fdt;
> +       int ret;
> +       u32 size;
> +       struct device_node *overlay_root;
> +
> +       *ovcs_id = 0;
> +       ret = 0;
> +
> +       if (fdt_check_header(overlay_fdt)) {
> +               pr_err("Invalid overlay_fdt header\n");
> +               return -EINVAL;
> +       }
> +
> +       size = fdt_totalsize(overlay_fdt);
> +
> +       /*
> +        * Must create permanent copy of FDT because of_fdt_unflatten_tree()
> +        * will create pointers to the passed in FDT in the unflattened tree.
> +        */
> +       new_fdt = kmemdup(overlay_fdt, size, GFP_KERNEL);
> +       if (!new_fdt)
> +               return -ENOMEM;
> +
> +       of_fdt_unflatten_tree(new_fdt, NULL, &overlay_root);
> +       if (!overlay_root) {
> +               pr_err("unable to unflatten overlay_fdt\n");
> +               ret = -EINVAL;
> +               goto out_free_new_fdt;
> +       }
> +
> +       ret = of_overlay_apply(new_fdt, overlay_root, ovcs_id);
> +       if (ret < 0) {
> +               /*
> +                * new_fdt and overlay_root now belong to the overlay
> +                * changeset.
> +                * overlay changeset code is responsible for freeing them.
> +                */
> +               goto out;
> +       }
> +
> +       return 0;
> +
> +
> +out_free_new_fdt:
> +       kfree(new_fdt);
> +
> +out:
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(of_overlay_fdt_apply);

[...]

> diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
> index 7a9abaae874d..2d706039ac96 100644
> --- a/drivers/of/unittest.c
> +++ b/drivers/of/unittest.c
> @@ -45,6 +45,8 @@
>         failed; \
>  })
>
> +static int __init overlay_data_apply(const char *overlay_name, int *overlay_id);
> +
>  static void __init of_unittest_find_node_by_name(void)
>  {
>         struct device_node *np;
> @@ -997,8 +999,7 @@ static int __init unittest_data_add(void)
>         }
>
>         /*
> -        * This lock normally encloses of_overlay_apply() as well as
> -        * of_resolve_phandles().
> +        * This lock normally encloses of_resolve_phandles()

I thought this lock was going to be internal when we did this change.

>          */
>         of_overlay_mutex_lock();
>
> @@ -1191,12 +1192,12 @@ static int of_unittest_device_exists(int unittest_nr, enum overlay_type ovtype)
>         return 0;
>  }
>
> -static const char *overlay_path(int nr)
> +static const char *overlay_name_from_nr(int nr)
>  {
>         static char buf[256];
>
>         snprintf(buf, sizeof(buf) - 1,
> -               "/testcase-data/overlay%d", nr);
> +               "overlay_%d", nr);
>         buf[sizeof(buf) - 1] = '\0';
>
>         return buf;
> @@ -1263,25 +1264,19 @@ static void of_unittest_destroy_tracked_overlays(void)
>         } while (defers > 0);
>  }
>
> -static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr,
> +static int __init of_unittest_apply_overlay(int overlay_nr, int unittest_nr,
>                 int *overlay_id)

Why __init? Really, we want to move towards building the unittests as
a module and we don't want __init for that. Though maybe __init is a
nop for modules, I don't remember offhand.

In any case, seems like a separate change.

Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux