Release previously merged options from merge_opts(), reduces memory-usage of iptables-restore dramatically (Pablo Neira)
diff --git a/iptables.c b/iptables.c
index 27c5cfa..33ee337 100644
--- a/iptables.c
+++ b/iptables.c
@@ -306,6 +306,16 @@
dst->s_addr = src->s_addr;
}
+static void free_opts(int reset_offset)
+{
+ if (opts != original_opts) {
+ free(opts);
+ opts = original_opts;
+ if (reset_offset)
+ global_option_offset = 0;
+ }
+}
+
void
exit_error(enum exittype status, char *msg, ...)
{
@@ -321,6 +331,8 @@
if (status == VERSION_PROBLEM)
fprintf(stderr,
"Perhaps iptables or your kernel needs to be upgraded.\n");
+ /* On error paths, make sure that we don't leak memory */
+ free_opts(1);
exit(status);
}
@@ -331,6 +343,7 @@
fprintf(stderr, "Error occurred at line: %d\n", line);
fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
program_name, program_name );
+ free_opts(1);
exit(status);
}
@@ -1016,6 +1029,9 @@
unsigned int num_old, num_new, i;
struct option *merge;
+ /* Release previous options merged if any */
+ free_opts(0);
+
for (num_old = 0; oldopts[num_old].name; num_old++);
for (num_new = 0; newopts[num_new].name; num_new++);
@@ -2443,12 +2459,7 @@
free(saddrs);
free(daddrs);
-
- if (opts != original_opts) {
- free(opts);
- opts = original_opts;
- global_option_offset = 0;
- }
+ free_opts(1);
return ret;
}