cgroup: Fix memory leak when parsing multiple source parameters
[linux-2.6-microblaze.git] / kernel / cgroup / cgroup-v1.c
index f2d7cea..32596fd 100644 (file)
@@ -38,10 +38,7 @@ static bool cgroup_no_v1_named;
  */
 static struct workqueue_struct *cgroup_pidlist_destroy_wq;
 
-/*
- * Protects cgroup_subsys->release_agent_path.  Modifying it also requires
- * cgroup_mutex.  Reading requires either cgroup_mutex or this spinlock.
- */
+/* protects cgroup_subsys->release_agent_path */
 static DEFINE_SPINLOCK(release_agent_path_lock);
 
 bool cgroup1_ssid_disabled(int ssid)
@@ -775,22 +772,29 @@ void cgroup1_release_agent(struct work_struct *work)
 {
        struct cgroup *cgrp =
                container_of(work, struct cgroup, release_agent_work);
-       char *pathbuf = NULL, *agentbuf = NULL;
+       char *pathbuf, *agentbuf;
        char *argv[3], *envp[3];
        int ret;
 
-       mutex_lock(&cgroup_mutex);
+       /* snoop agent path and exit early if empty */
+       if (!cgrp->root->release_agent_path[0])
+               return;
 
+       /* prepare argument buffers */
        pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
-       agentbuf = kstrdup(cgrp->root->release_agent_path, GFP_KERNEL);
-       if (!pathbuf || !agentbuf || !strlen(agentbuf))
-               goto out;
+       agentbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+       if (!pathbuf || !agentbuf)
+               goto out_free;
 
-       spin_lock_irq(&css_set_lock);
-       ret = cgroup_path_ns_locked(cgrp, pathbuf, PATH_MAX, &init_cgroup_ns);
-       spin_unlock_irq(&css_set_lock);
+       spin_lock(&release_agent_path_lock);
+       strlcpy(agentbuf, cgrp->root->release_agent_path, PATH_MAX);
+       spin_unlock(&release_agent_path_lock);
+       if (!agentbuf[0])
+               goto out_free;
+
+       ret = cgroup_path_ns(cgrp, pathbuf, PATH_MAX, &init_cgroup_ns);
        if (ret < 0 || ret >= PATH_MAX)
-               goto out;
+               goto out_free;
 
        argv[0] = agentbuf;
        argv[1] = pathbuf;
@@ -801,11 +805,7 @@ void cgroup1_release_agent(struct work_struct *work)
        envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
        envp[2] = NULL;
 
-       mutex_unlock(&cgroup_mutex);
        call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
-       goto out_free;
-out:
-       mutex_unlock(&cgroup_mutex);
 out_free:
        kfree(agentbuf);
        kfree(pathbuf);
@@ -908,6 +908,8 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
        opt = fs_parse(fc, cgroup1_fs_parameters, param, &result);
        if (opt == -ENOPARAM) {
                if (strcmp(param->key, "source") == 0) {
+                       if (fc->source)
+                               return invalf(fc, "Multiple sources not supported");
                        fc->source = param->string;
                        param->string = NULL;
                        return 0;