network: fix memory leak when an netdev was skipped
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 20 Dec 2017 09:17:37 +0000 (10:17 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 20 Dec 2017 15:17:29 +0000 (16:17 +0100)
In general we'd leak anything that was allocated in the first parsing of
netdev, e.g. netdev name, host name, etc. Use normal netdev_unref to make sure
everything is freed.

--- command ---
/home/zbyszek/src/systemd/build2/test-network
--- stderr ---
/etc/systemd/network/wg0.netdev:3: Failed to parse netdev kind, ignoring: wireguard
/etc/systemd/network/wg0.netdev:5: Unknown section 'WireGuard'. Ignoring.
/etc/systemd/network/wg0.netdev:9: Unknown section 'WireGuardPeer'. Ignoring.
NetDev has no Kind configured in /etc/systemd/network/wg0.netdev. Ignoring
/etc/systemd/network/br0.network:13: Unknown lvalue 'NetDev' in section 'Network'
br0: netdev ready

=================================================================
==11666==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4 byte(s) in 1 object(s) allocated from:
    #0 0x7f3a314cf238 in __interceptor_strdup (/lib64/libasan.so.4+0x77238)
    #1 0x7f3a30e71ad1 in free_and_strdup ../src/basic/string-util.c:870
    #2 0x7f3a30d34fba in config_parse_ifname ../src/shared/conf-parser.c:981
    #3 0x7f3a30d2f5b0 in next_assignment ../src/shared/conf-parser.c:155
    #4 0x7f3a30d30303 in parse_line ../src/shared/conf-parser.c:273
    #5 0x7f3a30d30dee in config_parse ../src/shared/conf-parser.c:390
    #6 0x7f3a30d310a5 in config_parse_many_files ../src/shared/conf-parser.c:428
    #7 0x7f3a30d3181c in config_parse_many ../src/shared/conf-parser.c:487
    #8 0x55b4200f9b00 in netdev_load_one ../src/network/netdev/netdev.c:634
    #9 0x55b4200fb562 in netdev_load ../src/network/netdev/netdev.c:778
    #10 0x55b4200c607a in manager_load_config ../src/network/networkd-manager.c:1299
    #11 0x55b4200818e0 in test_load_config ../src/network/test-network.c:128
    #12 0x55b42008343b in main ../src/network/test-network.c:254
    #13 0x7f3a305f8889 in __libc_start_main (/lib64/libc.so.6+0x20889)

SUMMARY: AddressSanitizer: 4 byte(s) leaked in 1 allocation(s).
-------

src/network/netdev/netdev.c
src/network/netdev/netdev.h

index 5530760..7afe30f 100644 (file)
@@ -138,7 +138,7 @@ static void netdev_free(NetDev *netdev) {
 
         netdev_cancel_callbacks(netdev);
 
-        if (netdev->ifname)
+        if (netdev->ifname && netdev->manager)
                 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
 
         free(netdev->filename);
@@ -601,8 +601,7 @@ int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callbac
 }
 
 static int netdev_load_one(Manager *manager, const char *filename) {
-        _cleanup_netdev_unref_ NetDev *netdev = NULL;
-        _cleanup_free_ NetDev *netdev_raw = NULL;
+        _cleanup_netdev_unref_ NetDev *netdev_raw = NULL, *netdev = NULL;
         _cleanup_fclose_ FILE *file = NULL;
         const char *dropin_dirname;
         bool independent = false;
@@ -628,9 +627,11 @@ static int netdev_load_one(Manager *manager, const char *filename) {
         if (!netdev_raw)
                 return log_oom();
 
+        netdev_raw->n_ref = 1;
         netdev_raw->kind = _NETDEV_KIND_INVALID;
-        dropin_dirname = strjoina(basename(filename), ".d");
+        netdev_raw->state = _NETDEV_STATE_INVALID;
 
+        dropin_dirname = strjoina(basename(filename), ".d");
         r = config_parse_many(filename, network_dirs, dropin_dirname,
                               "Match\0NetDev\0",
                               config_item_perf_lookup, network_netdev_gperf_lookup,
@@ -638,10 +639,6 @@ static int netdev_load_one(Manager *manager, const char *filename) {
         if (r < 0)
                 return r;
 
-        r = fseek(file, 0, SEEK_SET);
-        if (r < 0)
-                return -errno;
-
         /* skip out early if configuration does not match the environment */
         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
                              netdev_raw->match_host, netdev_raw->match_virt,
@@ -659,15 +656,18 @@ static int netdev_load_one(Manager *manager, const char *filename) {
                 return 0;
         }
 
+        r = fseek(file, 0, SEEK_SET);
+        if (r < 0)
+                return -errno;
+
         netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
         if (!netdev)
                 return log_oom();
 
         netdev->n_ref = 1;
         netdev->manager = manager;
-        netdev->state = _NETDEV_STATE_INVALID;
         netdev->kind = netdev_raw->kind;
-        netdev->ifname = netdev_raw->ifname;
+        netdev->state = _NETDEV_STATE_INVALID;
 
         if (NETDEV_VTABLE(netdev)->init)
                 NETDEV_VTABLE(netdev)->init(netdev);
index ec65251..24915b2 100644 (file)
@@ -143,7 +143,7 @@ typedef struct NetDevVTable {
 
 extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
 
-#define NETDEV_VTABLE(n) netdev_vtable[(n)->kind]
+#define NETDEV_VTABLE(n) ((n)->kind != _NETDEV_KIND_INVALID ? netdev_vtable[(n)->kind] : NULL)
 
 /* For casting a netdev into the various netdev kinds */
 #define DEFINE_NETDEV_CAST(UPPERCASE, MixedCase)                            \