[RFC 01/15] scripts/dtc: fix most memory leaks in dtc

Fabien Parent fparent at baylibre.com
Thu Oct 3 10:26:13 EDT 2013


Looping back the mailing-lists.

On Thu, Oct 3, 2013 at 4:23 PM, Fabien Parent <fparent at baylibre.com> wrote:
>
> Hi David,
>
>
> On Wed, Oct 2, 2013 at 2:59 PM, David Gibson <david at gibson.dropbear.id.au> wrote:
>>
>> On Tue, Sep 24, 2013 at 06:52:07PM +0200, Benoit Cousson wrote:
>> > From: Fabien Parent <fparent at baylibre.com>
>>
>> As noted elsewhere, please write patches against upstream dtc, not the
>> version in the kernel.  git://git.kernel.org/pub/scm/utils/dtc/dtc.git
>>
>> > There are a few memory leaks in dtc which until now were not that important
>> > since they were all in the parser and only one instance of the parser was run
>> > per instance of dtc. The following commits will add a validation of dts through
>> > schema which have the same syntax as dts, i.e. the parser of dts will be reused
>> > to parse schema. The consequence is that instead of having the parser running
>> > only one time for an instance of the dtc process, the parser will run
>> > many many times and thus the leak that were not important until now becomes
>> > urgent to fix.
>>
>> Hrm, yeah.  I'm aware that I haven't been very careful with memory
>> leaks within the parser.  Essentially, I've been treating the process
>> as a pool allocator with exactly one pool - I've even considered
>> getting rid of those free()s we do have.
>>
>> If the parser's going to be invoked repeatedly, then, yes, that
>> certainly needs fixing.  Whether individually fixing each leak or
>> using a explicit pool allocator is a better option is less clear.
>
>
> I've chosen the path of using free()s since it was removing most (all?) memory leaks from
> the parser and wasn't adding much more complexity to it. I don't think a pool allocator would
> be useful to DTC in its current state, but it's true that with schemas which are using
> the DTS syntax it could make a lot of sense to switch to a pool allocator.
>
> I guess I will wait until the discussion about this proposal has progressed and see
> whether this patch should be rewritten using a pool allocator or not.
>
>>
>> > dtc-lexer: Do not duplicate the string which contains literals because the
>> > string is directly converted afterward to an integer and is never used again.
>> > livetree: Add a bunch of free helper functions to clean properly the
>> > dt.
>>
>> This is no good.  Making this assumption is a layering violation - if
>> the parser was changed so that this literal wasn't converted until
>> after another token was read, the yytext value copied in here would no
>> longer be valid and would break horribly.
>>
>
> Right, I've been lazy on this one and I took the easy road.
>
>
>>
>> >
>> > Signed-off-by: Fabien Parent <fparent at baylibre.com>
>> > Signed-off-by: Benoit Cousson <bcousson at baylibre.com>
>> > ---
>> >  scripts/dtc/dtc-lexer.l             |   2 +-
>> >  scripts/dtc/dtc-lexer.lex.c_shipped |   2 +-
>> >  scripts/dtc/dtc.c                   |   1 +
>> >  scripts/dtc/dtc.h                   |   1 +
>> >  scripts/dtc/livetree.c              | 108 +++++++++++++++++++++++++++++++++---
>> >  5 files changed, 105 insertions(+), 9 deletions(-)
>> >
>> > diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l
>> > index 3b41bfc..4f63fbf 100644
>> > --- a/scripts/dtc/dtc-lexer.l
>> > +++ b/scripts/dtc/dtc-lexer.l
>> > @@ -146,7 +146,7 @@ static int pop_input_file(void);
>> >               }
>> >
>> >  <V1>([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? {
>> > -                     yylval.literal = xstrdup(yytext);
>> > +                     yylval.literal = yytext;
>> >                       DPRINT("Literal: '%s'\n", yylval.literal);
>> >                       return DT_LITERAL;
>> >               }
>> > diff --git a/scripts/dtc/dtc-lexer.lex.c_shipped b/scripts/dtc/dtc-lexer.lex.c_shipped
>> > index 2d30f41..5c0d27c 100644
>> > --- a/scripts/dtc/dtc-lexer.lex.c_shipped
>> > +++ b/scripts/dtc/dtc-lexer.lex.c_shipped
>> > @@ -1054,7 +1054,7 @@ case 10:
>> >  YY_RULE_SETUP
>> >  #line 148 "dtc-lexer.l"
>> >  {
>> > -                     yylval.literal = xstrdup(yytext);
>> > +                     yylval.literal = yytext;
>> >                       DPRINT("Literal: '%s'\n", yylval.literal);
>> >                       return DT_LITERAL;
>> >               }
>> > diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c
>> > index a375683..215ae92 100644
>> > --- a/scripts/dtc/dtc.c
>> > +++ b/scripts/dtc/dtc.c
>> > @@ -256,5 +256,6 @@ int main(int argc, char *argv[])
>> >               die("Unknown output format \"%s\"\n", outform);
>> >       }
>> >
>> > +     free_dt(bi);
>> >       exit(0);
>> >  }
>> > diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h
>> > index 3e42a07..9c45fd2 100644
>> > --- a/scripts/dtc/dtc.h
>> > +++ b/scripts/dtc/dtc.h
>> > @@ -245,6 +245,7 @@ struct boot_info {
>> >  struct boot_info *build_boot_info(struct reserve_info *reservelist,
>> >                                 struct node *tree, uint32_t boot_cpuid_phys);
>> >  void sort_tree(struct boot_info *bi);
>> > +void free_dt(struct boot_info *bi);
>> >
>> >  /* Checks */
>> >
>> > diff --git a/scripts/dtc/livetree.c b/scripts/dtc/livetree.c
>> > index b61465f..5c8692c 100644
>> > --- a/scripts/dtc/livetree.c
>> > +++ b/scripts/dtc/livetree.c
>> > @@ -20,6 +20,10 @@
>> >
>> >  #include "dtc.h"
>> >
>> > +static void free_node_list(struct node *n);
>> > +static void free_node(struct node *n);
>> > +static void free_property(struct property *p);
>> > +
>> >  /*
>> >   * Tree building functions
>> >   */
>> > @@ -144,7 +148,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
>> >
>> >       /* Add new node labels to old node */
>> >       for_each_label_withdel(new_node->labels, l)
>> > -             add_label(&old_node->labels, l->label);
>> > +             add_label(&old_node->labels, xstrdup(l->label));
>> >
>> >       /* Move properties from the new node to the old node.  If there
>> >        * is a collision, replace the old value with the new */
>> > @@ -156,7 +160,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
>> >
>> >               if (new_prop->deleted) {
>> >                       delete_property_by_name(old_node, new_prop->name);
>> > -                     free(new_prop);
>> > +                     free_property(new_prop);
>> >                       continue;
>> >               }
>> >
>> > @@ -165,7 +169,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
>> >                       if (streq(old_prop->name, new_prop->name)) {
>> >                               /* Add new labels to old property */
>> >                               for_each_label_withdel(new_prop->labels, l)
>> > -                                     add_label(&old_prop->labels, l->label);
>> > +                                     add_label(&old_prop->labels, xstrdup(l->label));
>> >
>> >                               old_prop->val = new_prop->val;
>> >                               old_prop->deleted = 0;
>> > @@ -191,7 +195,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
>> >
>> >               if (new_child->deleted) {
>> >                       delete_node_by_name(old_node, new_child->name);
>> > -                     free(new_child);
>> > +                     free_node(new_child);
>> >                       continue;
>> >               }
>> >
>> > @@ -211,7 +215,7 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node)
>> >
>> >       /* The new node contents are now merged into the old node.  Free
>> >        * the new node. */
>> > -     free(new_node);
>> > +     free_node(new_node);
>> >
>> >       return old_node;
>> >  }
>> > @@ -532,13 +536,13 @@ cell_t get_node_phandle(struct node *root, struct node *node)
>> >       if (!get_property(node, "linux,phandle")
>> >           && (phandle_format & PHANDLE_LEGACY))
>> >               add_property(node,
>> > -                          build_property("linux,phandle",
>> > +                          build_property(xstrdup("linux,phandle"),
>> >                                           data_append_cell(empty_data, phandle)));
>> >
>> >       if (!get_property(node, "phandle")
>> >           && (phandle_format & PHANDLE_EPAPR))
>> >               add_property(node,
>> > -                          build_property("phandle",
>> > +                          build_property(xstrdup("phandle"),
>> >                                           data_append_cell(empty_data, phandle)));
>> >
>> >       /* If the node *does* have a phandle property, we must
>> > @@ -707,3 +711,93 @@ void sort_tree(struct boot_info *bi)
>> >       sort_reserve_entries(bi);
>> >       sort_node(bi->dt);
>> >  }
>> > +
>> > +static void free_marker_list(struct marker *m)
>> > +{
>> > +     struct marker *marker, *marker_next;
>> > +
>> > +     if (!m)
>> > +             return;
>> > +
>> > +     for (marker = m, marker_next = marker ? marker->next : NULL;
>> > +          marker;
>> > +          marker = marker_next, marker_next = marker ? marker->next : NULL) {
>>
>> Rather hard to read version of safe-against-free list iteration.
>>
>
> Agreed.
>
>>
>> > +             free(marker->ref);
>> > +             free(marker);
>> > +     }
>> > +}
>> > +
>> > +static void free_label_list(struct label *l)
>> > +{
>> > +     struct label *label, *label_next;
>> > +
>> > +     if (!l)
>> > +             return;
>> > +
>> > +     for (label = l, label_next = label ? label->next : NULL;
>> > +          label;
>> > +          label = label_next, label_next = label ? label->next : NULL) {
>> > +             free(label->label);
>> > +             free(label);
>> > +     }
>> > +}
>> > +
>> > +static void free_property(struct property *p)
>> > +{
>> > +     if (!p)
>> > +             return;
>> > +
>> > +     free_label_list(p->labels);
>> > +     free_marker_list(p->val.markers);
>> > +     free(p->val.val);
>> > +     free(p->name);
>> > +     free(p);
>> > +}
>> > +
>> > +static void free_property_list(struct property *p)
>> > +{
>> > +     struct property *next;
>> > +
>> > +     if (!p)
>> > +             return;
>> > +
>> > +     for (next = p->next; p; p = next, next = p ? p->next : NULL)
>> > +             free_property(p);
>> > +}
>> > +
>> > +static void free_node(struct node *n)
>> > +{
>> > +     if (!n)
>> > +             return;
>> > +
>> > +     free_node_list(n->children);
>> > +     free_label_list(n->labels);
>> > +     free_property_list(n->proplist);
>> > +     free(n->fullpath);
>> > +     if (n->name && *n->name)
>>
>> *n->name .. so, the name can (and must) be statically allocated if
>> it's exactly "".  Not a useful optimization, I think.
>
>
> True.
>
>>
>>
>> > +             free(n->name);
>> > +     free(n);
>> > +}
>> > +
>> > +static void free_node_list(struct node *n)
>> > +{
>> > +     struct node *next;
>> > +
>> > +     if (!n)
>> > +             return;
>> > +
>> > +     for (next = n->next_sibling;
>> > +          n;
>> > +          n = next, next = n ? n->next_sibling : NULL) {
>> > +             free_node(n);
>> > +     }
>> > +}
>> > +
>> > +void free_dt(struct boot_info *bi)
>> > +{
>> > +     if (!bi)
>> > +             return;
>> > +
>> > +     free_node_list(bi->dt);
>> > +     free(bi);
>> > +}
>>
>> --
>> David Gibson                    | I'll have my music baroque, and my code
>> david AT gibson.dropbear.id.au  | minimalist, thank you.  NOT _the_ _other_
>>                                 | _way_ _around_!
>> http://www.ozlabs.org/~dgibson
>
>



More information about the linux-arm-kernel mailing list