Additional patch that reduces some repeating parts. On 10/25/2018 10:36 AM, Petr Mensik wrote: > Hi again. > > This time I have a little bit more controversal patches. But I think > still useful. They fixes memory leaks that might occur in some cases. > Most dnsmasq errors is fatal, so it does not matter. But some are not. > Some parts are reloaded on SIGHUP signal, so it might leak more than once. > > Some example when it changes the failures. Use dhcp-options file with > this content: > > tag:error,vendor:redhat > option:ntp-server,1.2.3.4.5 > option6:ntp-server,[:::] > > Is not fatal and dnsmasq will start. On each reload command, it would > leak some memory. I validated it using valgrind --leak-check=full > dnsmasq -d. This patch fixes it. It introduces something that might be > considered constructor and destructor of selected structures. What do > you think of it? > > Comments are welcome. Another patch would be sent short after, they are > too big together to require moderator attention. > > Cheers, > Petr > > > > _______________________________________________ > Dnsmasq-discuss mailing list > Dnsmasq-discuss@lists.thekelleys.org.uk > http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss >
-- Petr Menšík Software Engineer Red Hat, http://www.redhat.com/ email: pemen...@redhat.com PGP: 65C6C973
>From ae3837cabc7e7b2fbd2d875f403a3f26a4d81422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com> Date: Thu, 16 Aug 2018 18:10:25 +0200 Subject: [PATCH 2/2] Reduce repeating code parsing tags DHCP parameters often can have optional tags before it. Reduce repeated parsing of it, use dhcp_tags() to parse and free if unsuccessful. Make sure null pointer will not crash free function. Free also errors in dhcp-range. --- src/option.c | 326 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 171 insertions(+), 155 deletions(-) diff --git a/src/option.c b/src/option.c index 547d36e..66847fb 100644 --- a/src/option.c +++ b/src/option.c @@ -577,14 +577,15 @@ static void *opt_malloc(size_t size) return ret; } -static char *opt_string_alloc(char *cp) +static char *opt_string_alloc(const char *cp) { char *ret = NULL; + size_t len; - if (cp && strlen(cp) != 0) + if (cp && (len = strlen(cp)) != 0) { - ret = opt_malloc(strlen(cp)+1); - strcpy(ret, cp); + ret = opt_malloc(len+1); + memcpy(ret, cp, len+1); /* restore hidden metachars */ unhide_metas(ret); @@ -760,6 +761,7 @@ static void do_usage(void) #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0) #define ret_err_free(x,m) do { strcpy(errstr, (x)); free((m)); return 0; } while (0) +#define goto_err(x) do { strcpy(errstr, (x)); goto on_error; } while (0) static char *parse_mysockaddr(char *arg, union mysockaddr *addr) { @@ -961,6 +963,97 @@ static char *set_prefix(char *arg) return arg; } +static struct dhcp_netid * +dhcp_netid_create(const char *net, struct dhcp_netid *next) +{ + struct dhcp_netid *tt; + tt = opt_malloc(sizeof (struct dhcp_netid)); + tt->net = opt_string_alloc(net); + tt->next = next; + return tt; +} + +static void dhcp_netid_free(struct dhcp_netid *nid) +{ + while (nid) + { + struct dhcp_netid *tmp = nid; + nid = nid->next; + free(tmp->net); + free(tmp); + } +} + +/* Parse one or more tag:s before parameters. + * Moves arg to the end of tags. */ +static struct dhcp_netid * dhcp_tags(char **arg) +{ + struct dhcp_netid *id = NULL; + + while (is_tag_prefix(*arg)) + { + char *comma = split(*arg); + id = dhcp_netid_create((*arg)+4, id); + *arg = comma; + }; + if (!*arg) + { + dhcp_netid_free(id); + id = NULL; + } + return id; +} + +static void dhcp_netid_list_free(struct dhcp_netid_list *netid) +{ + while (netid) + { + struct dhcp_netid_list *tmplist = netid; + netid = netid->next; + dhcp_netid_free(tmplist->list); + free(tmplist); + } +} + +static void dhcp_config_free(struct dhcp_config *config) +{ + if (config) + { + struct hwaddr_config *hwaddr = config->hwaddr; + while (hwaddr) + { + struct hwaddr_config *tmp = hwaddr; + hwaddr = hwaddr->next; + free(tmp); + } + dhcp_netid_list_free(config->netid); + if (config->flags & CONFIG_CLID) + free(config->clid); + free(config); + } +} + +static void dhcp_context_free(struct dhcp_context *ctx) +{ + if (ctx) + { + dhcp_netid_free(ctx->filter); + free(ctx->netid.net); + free(ctx->template_interface); + free(ctx); + } +} + +static void dhcp_opt_free(struct dhcp_opt *opt) +{ + if (opt->flags & DHOPT_VENDOR) + free(opt->u.vendor_class); + dhcp_netid_free(opt->netid); + free(opt->val); + free(opt); +} + + /* This is too insanely large to keep in-line in the switch */ static int parse_dhcp_opt(char *errstr, char *arg, int flags) { @@ -968,7 +1061,6 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) char lenchar = 0, *cp; int addrs, digs, is_addr, is_addr6, is_hex, is_dec, is_string, dots; char *comma = NULL; - struct dhcp_netid *np = NULL; u16 opt_len = 0; int is6 = 0; int option_ok = 0; @@ -1055,14 +1147,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) } else { - new->netid = opt_malloc(sizeof (struct dhcp_netid)); /* allow optional "net:" or "tag:" for consistency */ - if (is_tag_prefix(arg)) - new->netid->net = opt_string_alloc(arg+4); - else - new->netid->net = opt_string_alloc(set_prefix(arg)); - new->netid->next = np; - np = new->netid; + const char *name = (is_tag_prefix(arg)) ? arg+4 : set_prefix(arg); + new->netid = dhcp_netid_create(name, new->netid); } arg = comma; @@ -1072,7 +1159,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) if (is6) { if (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE)) - ret_err_free(_("unsupported encapsulation for IPv6 option"), new); + goto_err(_("unsupported encapsulation for IPv6 option")); if (opt_len == 0 && !(new->flags & DHOPT_RFC3925)) @@ -1086,7 +1173,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) /* option may be missing with rfc3925 match */ if (!option_ok) - ret_err_free(_("bad dhcp-option"), new); + goto_err(_("bad dhcp-option")); if (comma) { @@ -1154,10 +1241,10 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) is_string = is_dec = is_hex = 0; if (!is6 && (!is_addr || dots == 0)) - ret_err_free(_("bad IP address"), new); + goto_err(_("bad IP address")); if (is6 && !is_addr6) - ret_err_free(_("bad IPv6 address"), new); + goto_err(_("bad IPv6 address")); } /* or names */ else if (opt_len & (OT_NAME | OT_RFC1035_NAME | OT_CSTRING)) @@ -1250,10 +1337,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) comma = split(cp); slash = split_chr(cp, '/'); if (!inet_pton(AF_INET, cp, &in)) - { - free(new->val); - ret_err_free(_("bad IPv4 address"), new); - } + goto_err(_("bad IPv4 address")); if (!slash) { memcpy(op, &in, INADDRSZ); @@ -1299,8 +1383,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) continue; } - free(new->val); - ret_err_free(_("bad IPv6 address"), new); + goto_err(_("bad IPv6 address")); } new->len = op - new->val; } @@ -1327,7 +1410,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) if (strcmp (arg, ".") != 0) { if (!(dom = canonicalise_opt(arg))) - ret_err_free(_("bad domain in dhcp-option"), new); + goto_err(_("bad domain in dhcp-option")); domlen = strlen(dom) + 2; } @@ -1421,7 +1504,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) { char *dom = canonicalise_opt(arg); if (!dom) - ret_err_free(_("bad domain in dhcp-option"), new); + goto_err(_("bad domain in dhcp-option")); newp = opt_malloc(len + strlen(dom) + 2); @@ -1459,14 +1542,14 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) ((new->len > 255) || (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))) || (new->len > 250 && (new->flags & DHOPT_RFC3925)))) - ret_err_free(_("dhcp-option too long"), new); + goto_err(_("dhcp-option too long")); if (flags == DHOPT_MATCH) { if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) || !new->netid || new->netid->next) - ret_err_free(_("illegal dhcp-match"), new); + goto_err(_("illegal dhcp-match")); if (is6) { @@ -1491,6 +1574,9 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags) } return 1; +on_error: + dhcp_opt_free(new); + return 0; } #endif @@ -1521,43 +1607,6 @@ static void server_list_free(struct server *list) } } -static void dhcp_netid_free(struct dhcp_netid *nid) -{ - while (nid) - { - struct dhcp_netid *tmp = nid; - nid = nid->next; - free(tmp->net); - free(tmp); - } -} - -static void dhcp_netid_list_free(struct dhcp_netid_list *netid) -{ - while (netid) - { - struct dhcp_netid_list *tmplist = netid; - netid = netid->next; - dhcp_netid_free(tmplist->list); - free(tmplist); - } -} - -static void dhcp_config_free(struct dhcp_config *config) -{ - struct hwaddr_config *hwaddr = config->hwaddr; - while (hwaddr) - { - struct hwaddr_config *tmp = hwaddr; - hwaddr = hwaddr->next; - free(tmp); - } - dhcp_netid_list_free(config->netid); - if (config->flags & CONFIG_CLID) - free(config->clid); - free(config); -} - static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only) { int i; @@ -2849,26 +2898,19 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma { if (is_tag_prefix(arg)) { - char *net = arg+4; - /* ignore empty tag */ - if (*net) - { - struct dhcp_netid *tt; - tt = opt_malloc(sizeof (struct dhcp_netid)); - tt->net = opt_string_alloc(net); - tt->next = new->filter; - new->filter = tt; - } + if (arg[4]) + new->filter = dhcp_netid_create(arg+4, new->filter); } else { if (new->netid.net) - ret_err_free(_("only one tag allowed"), new); - else if (strstr(arg, "set:") == arg) - new->netid.net = opt_string_alloc(arg+4); + { + dhcp_context_free(new); + ret_err(_("only one tag allowed")); + } else - new->netid.net = opt_string_alloc(arg); + new->netid.net = opt_string_alloc(set_prefix(arg)); } arg = comma; } @@ -2884,7 +2926,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma break; if (k < 2) - ret_err_free(_("bad dhcp-range"), new); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (inet_pton(AF_INET, a[0], &new->start)) { @@ -2896,7 +2941,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma else if (strcmp(a[1], "proxy") == 0) new->flags |= CONTEXT_PROXY; else if (!inet_pton(AF_INET, a[1], &new->end)) - ret_err_free(_("bad dhcp-range"), new); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr)) { @@ -2911,7 +2959,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->flags |= CONTEXT_NETMASK; leasepos = 3; if (!is_same_net(new->start, new->end, new->netmask)) - ret_err_free(_("inconsistent DHCP range"), new); + { + dhcp_context_free(new); + ret_err(_("inconsistent DHCP range")); + } if (k >= 4 && strchr(a[3], '.') && @@ -2925,6 +2976,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma #ifdef HAVE_DHCP6 else if (inet_pton(AF_INET6, a[0], &new->start6)) { + const char *err = NULL; + new->flags |= CONTEXT_V6; new->prefix = 64; /* default */ new->end6 = new->start6; @@ -2970,19 +3023,24 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } - if (new->prefix != 64) + if (new->prefix > 64) { if (new->flags & CONTEXT_RA) - ret_err_free(_("prefix length must be exactly 64 for RA subnets"), new); + err=(_("prefix length must be exactly 64 for RA subnets")); else if (new->flags & CONTEXT_TEMPLATE) - ret_err_free(_("prefix length must be exactly 64 for subnet constructors"), new); + err=(_("prefix length must be exactly 64 for subnet constructors")); } - - if (new->prefix < 64) - ret_err_free(_("prefix length must be at least 64"), new); + else if (new->prefix < 64) + err=(_("prefix length must be at least 64")); - if (!is_same_net6(&new->start6, &new->end6, new->prefix)) - ret_err_free(_("inconsistent DHCPv6 range"), new); + if (!err && !is_same_net6(&new->start6, &new->end6, new->prefix)) + err=(_("inconsistent DHCPv6 range")); + + if (err) + { + dhcp_context_free(new); + ret_err(err); + } /* dhcp-range=:: enables DHCP stateless on any interface */ if (IN6_IS_ADDR_UNSPECIFIED(&new->start6) && !(new->flags & CONTEXT_TEMPLATE)) @@ -2993,7 +3051,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma struct in6_addr zero; memset(&zero, 0, sizeof(zero)); if (!is_same_net6(&zero, &new->start6, new->prefix)) - ret_err_free(_("prefix must be zero with \"constructor:\" argument"), new); + { + dhcp_context_free(new); + ret_err(_("prefix must be zero with \"constructor:\" argument")); + } } if (addr6part(&new->start6) > addr6part(&new->end6)) @@ -3005,12 +3066,18 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } #endif else - ret_err_free(_("bad dhcp-range"), new); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (leasepos < k) { if (leasepos != k-1) - ret_err_free(_("bad dhcp-range"), new); + { + dhcp_context_free(new); + ret_err(_("bad dhcp-range")); + } if (strcmp(a[leasepos], "infinite") == 0) new->lease_time = 0xffffffff; @@ -3122,14 +3189,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma /* dhcp-host has strange backwards-compat needs. */ else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg) { - struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid)); struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list)); - newtag->net = opt_malloc(strlen(arg + 4) + 1); newlist->next = new->netid; new->netid = newlist; - newlist->list = newtag; - strcpy(newtag->net, arg+4); - unhide_metas(newtag->net); + newlist->list = dhcp_netid_create(arg+4, NULL); } else if (strstr(arg, "tag:") == arg) { @@ -3304,10 +3367,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } else { - struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid)); - newtag->net = opt_malloc(len - 3); - strcpy(newtag->net, arg+4); - unhide_metas(newtag->net); + struct dhcp_netid *newtag = dhcp_netid_create(arg+4, NULL); if (strstr(arg, "set:") == arg) { @@ -3324,7 +3384,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma else { new->set = NULL; - free(newtag); + dhcp_netid_free(newtag); break; } } @@ -3380,20 +3440,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case 'M': /* --dhcp-boot */ { - struct dhcp_netid *id = NULL; - while (is_tag_prefix(arg)) - { - struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid)); - newid->next = id; - id = newid; - comma = split(arg); - newid->net = opt_string_alloc(arg+4); - arg = comma; - }; + struct dhcp_netid *id = dhcp_tags(&arg); - if (!arg) + if (!id) { - dhcp_netid_free(id); ret_err(gen_err); } else @@ -3441,20 +3491,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_REPLY_DELAY: /* --dhcp-reply-delay */ { - struct dhcp_netid *id = NULL; - while (is_tag_prefix(arg)) - { - struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid)); - newid->next = id; - id = newid; - comma = split(arg); - newid->net = opt_string_alloc(arg+4); - arg = comma; - }; + struct dhcp_netid *id = dhcp_tags(&arg); - if (!arg) + if (!id) { - dhcp_netid_free(id); ret_err(gen_err); } else @@ -3481,21 +3521,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->netid = NULL; new->opt = 10; /* PXE_MENU_PROMPT */ - - while (is_tag_prefix(arg)) - { - struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); - comma = split(arg); - nn->next = new->netid; - new->netid = nn; - nn->net = opt_string_alloc(arg+4); - arg = comma; - } + new->netid = dhcp_tags(&arg); - if (!arg) + if (!new->netid) { - dhcp_netid_free(new->netid); - ret_err_free(gen_err, new); + dhcp_opt_free(new); + ret_err(gen_err); } else { @@ -3532,17 +3563,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->netid = NULL; new->sname = NULL; new->server.s_addr = 0; + new->netid = dhcp_tags(&arg); - while (is_tag_prefix(arg)) - { - struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid)); - comma = split(arg); - nn->next = new->netid; - new->netid = nn; - nn->net = opt_string_alloc(arg+4); - arg = comma; - } - if (arg && (comma = split(arg))) { for (i = 0; CSA[i]; i++) @@ -3776,14 +3798,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } while (arg) { - struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid)); comma = split(arg); - member->next = list; - list = member; - if (is_tag_prefix(arg)) - member->net = opt_string_alloc(arg+4); - else - member->net = opt_string_alloc(arg); + list = dhcp_netid_create(is_tag_prefix(arg) ? arg+4 :arg, list); arg = comma; } -- 2.14.4
_______________________________________________ Dnsmasq-discuss mailing list Dnsmasq-discuss@lists.thekelleys.org.uk http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss