[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] xenoprof fixes: active_domains races & cleanup



The active_domains code has race conditions:

* oprofile_set_active() calls set_active() method without holding
  start_sem.  This is clearly wrong, as xenoprof_set_active() makes
  several hypercalls.  oprofile_start(), for instance, could run in
  the middle of xenoprof_set_active().

* adomain_write(), adomain_read() and xenoprof_set_active() access
  global active_domains[] and adomains without synchronization.  I
  went for a simple, obvious fix and created another mutex.  Instead,
  one could move the shared data into oprof.c and protect it with
  start_sem, but that's more invasive.

Also clean up the code dealing with /dev/oprofile/active_domains:

* Use parameters instead of global variables to pass domain ids
  around.  Give those globals internal linkage.

* Allocate buffers dynamically to conserve stack space.

* Treat writes with size zero exactly like a write containing no
  domain id.  Before, zero-sized write was ignored, which is not the
  same.

* Parse domain ids as unsigned numbers.  Before, the first one was
  parsed as signed number.

  Because ispunct()-punctuation is ignored between domain ids, signs
  are still silently ignored except for the first number.  Hmm.

* Make parser accept whitespace as domain separator, because that's
  what you get when reading the file.

* EINVAL on domain ids overflowing domid_t.  Before, they were
  silently truncated.

* EINVAL on too many domain ids.  Before, the excess ones were
  silently ignored.

* Reset active domains on failure halfway through setting them.

* Fix potential buffer overflow in adomain_read().  Couldn't really
  happen because buffer was sufficient for current value of
  MAX_OPROF_DOMAINS.

Signed-off-by: Markus Armbruster <armbru@xxxxxxxxxx>

diff -x '*~' -rup linux-2.6.16.13-xen.patched-1/arch/i386/oprofile/xenoprof.c 
linux-2.6.16.13-xen.patched-4/arch/i386/oprofile/xenoprof.c
--- linux-2.6.16.13-xen.patched-1/arch/i386/oprofile/xenoprof.c 2006-05-15 
10:30:49.000000000 +0200
+++ linux-2.6.16.13-xen.patched-4/arch/i386/oprofile/xenoprof.c 2006-05-15 
10:31:23.000000000 +0200
@@ -289,9 +289,13 @@ static int xenoprof_set_active(int * act
 
        for (i=0; i<adomains; i++) {
                domid = active_domains[i];
+               if (domid != active_domains[i]) {
+                       ret = -EINVAL;
+                       goto out;
+               }
                ret = HYPERVISOR_xenoprof_op(XENOPROF_set_active, &domid);
                if (ret)
-                       return (ret);
+                       goto out;
                if (active_domains[i] == 0)
                        set_dom0 = 1;
        }
@@ -300,8 +304,11 @@ static int xenoprof_set_active(int * act
                domid = 0;
                ret = HYPERVISOR_xenoprof_op(XENOPROF_set_active, &domid);
        }
-       
-       active_defined = 1;
+
+out:
+       if (ret)
+               HYPERVISOR_xenoprof_op(XENOPROF_reset_active_list, NULL);
+       active_defined = !ret;
        return ret;
 }
 
diff -x '*~' -rup linux-2.6.16.13-xen.patched-1/drivers/oprofile/oprof.c 
linux-2.6.16.13-xen.patched-4/drivers/oprofile/oprof.c
--- linux-2.6.16.13-xen.patched-1/drivers/oprofile/oprof.c      2006-05-15 
10:28:11.000000000 +0200
+++ linux-2.6.16.13-xen.patched-4/drivers/oprofile/oprof.c      2006-05-15 
10:31:44.000000000 +0200
@@ -37,15 +37,17 @@ static DECLARE_MUTEX(start_sem);
  */
 static int timer = 0;
 
-extern unsigned int adomains;
-extern int active_domains[MAX_OPROF_DOMAINS];
-
-int oprofile_set_active(void)
+int oprofile_set_active(int active_domains[], unsigned int adomains)
 {
-       if (oprofile_ops.set_active)
-               return oprofile_ops.set_active(active_domains, adomains);
+       int err;
+
+       if (!oprofile_ops.set_active)
+               return -EINVAL;
 
-       return -EINVAL;
+       down(&start_sem);
+       err = oprofile_ops.set_active(active_domains, adomains);
+       up(&start_sem);
+       return err;
 }
 
 int oprofile_setup(void)
diff -x '*~' -rup linux-2.6.16.13-xen.patched-1/drivers/oprofile/oprof.h 
linux-2.6.16.13-xen.patched-4/drivers/oprofile/oprof.h
--- linux-2.6.16.13-xen.patched-1/drivers/oprofile/oprof.h      2006-05-15 
10:28:11.000000000 +0200
+++ linux-2.6.16.13-xen.patched-4/drivers/oprofile/oprof.h      2006-05-16 
13:32:42.000000000 +0200
@@ -36,6 +36,6 @@ void oprofile_timer_init(struct oprofile
 
 int oprofile_set_backtrace(unsigned long depth);
 
-int oprofile_set_active(void);
+int oprofile_set_active(int active_domains[], unsigned int adomains);
  
 #endif /* OPROF_H */
diff -x '*~' -rup 
linux-2.6.16.13-xen.patched-1/drivers/oprofile/oprofile_files.c 
linux-2.6.16.13-xen.patched-4/drivers/oprofile/oprofile_files.c
--- linux-2.6.16.13-xen.patched-1/drivers/oprofile/oprofile_files.c     
2006-05-15 10:28:11.000000000 +0200
+++ linux-2.6.16.13-xen.patched-4/drivers/oprofile/oprofile_files.c     
2006-05-16 13:42:58.000000000 +0200
@@ -126,63 +126,92 @@ static struct file_operations dump_fops 
 
 #define TMPBUFSIZE 512
 
-unsigned int adomains = 0;
-long active_domains[MAX_OPROF_DOMAINS];
+static unsigned int adomains = 0;
+static int active_domains[MAX_OPROF_DOMAINS + 1];
+static DEFINE_MUTEX(adom_mutex);
 
 static ssize_t adomain_write(struct file * file, char const __user * buf, 
                             size_t count, loff_t * offset)
 {
-       char tmpbuf[TMPBUFSIZE];
-       char * startp = tmpbuf;
-       char * endp = tmpbuf;
+       char *tmpbuf;
+       char *startp, *endp;
        int i;
        unsigned long val;
+       ssize_t retval = count;
        
        if (*offset)
                return -EINVAL; 
-       if (!count)
-               return 0;
        if (count > TMPBUFSIZE - 1)
                return -EINVAL;
 
-       memset(tmpbuf, 0x0, TMPBUFSIZE);
+       if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL)))
+               return -ENOMEM;
 
-       if (copy_from_user(tmpbuf, buf, count))
+       if (copy_from_user(tmpbuf, buf, count)) {
+               kfree(tmpbuf);
                return -EFAULT;
-       
-       for (i = 0; i < MAX_OPROF_DOMAINS; i++)
-               active_domains[i] = -1;
-       adomains = 0;
+       }
+       tmpbuf[count] = 0;
 
-       while (1) {
-               val = simple_strtol(startp, &endp, 0);
+       mutex_lock(&adom_mutex);
+
+       startp = tmpbuf;
+       /* Parse one more than MAX_OPROF_DOMAINS, for easy error checking */
+       for (i = 0; i <= MAX_OPROF_DOMAINS; i++) {
+               val = simple_strtoul(startp, &endp, 0);
                if (endp == startp)
                        break;
-               while (ispunct(*endp))
+               while (ispunct(*endp) || isspace(*endp))
                        endp++;
-               active_domains[adomains++] = val;
-               if (adomains >= MAX_OPROF_DOMAINS)
-                       break;
+               active_domains[i] = val;
+               if (active_domains[i] != val)
+                       /* Overflow, force error below */
+                       i = MAX_OPROF_DOMAINS + 1;
                startp = endp;
        }
-       if (oprofile_set_active())
-               return -EINVAL; 
-       return count;
+       /* Force error on trailing junk */
+       adomains = *startp ? MAX_OPROF_DOMAINS + 1 : i;
+
+       kfree(tmpbuf);
+
+       if (adomains > MAX_OPROF_DOMAINS
+           || oprofile_set_active(active_domains, adomains)) {
+               adomains = 0;
+               retval = -EINVAL;
+       }
+
+       mutex_unlock(&adom_mutex);
+       return retval;
 }
 
 static ssize_t adomain_read(struct file * file, char __user * buf, 
                            size_t count, loff_t * offset)
 {
-       char tmpbuf[TMPBUFSIZE];
-       size_t len = 0;
+       char * tmpbuf;
+       size_t len;
        int i;
-       /* This is all screwed up if we run out of space */
-       for (i = 0; i < adomains; i++) 
-               len += snprintf(tmpbuf + len, TMPBUFSIZE - len, 
-                               "%u ", (unsigned int)active_domains[i]);
-       len += snprintf(tmpbuf + len, TMPBUFSIZE - len, "\n");
-       return simple_read_from_buffer((void __user *)buf, count, 
-                                      offset, tmpbuf, len);
+       ssize_t retval;
+
+       if (!(tmpbuf = kmalloc(TMPBUFSIZE, GFP_KERNEL)))
+               return -ENOMEM;
+
+       mutex_lock(&adom_mutex);
+
+       len = 0;
+       for (i = 0; i < adomains; i++)
+               len += snprintf(tmpbuf + len,
+                               len < TMPBUFSIZE ? TMPBUFSIZE - len : 0,
+                               "%u ", active_domains[i]);
+       WARN_ON(len > TMPBUFSIZE);
+       if (len != 0 && len <= TMPBUFSIZE)
+               tmpbuf[len-1] = '\n';
+
+       mutex_unlock(&adom_mutex);
+
+       retval = simple_read_from_buffer(buf, count, offset, tmpbuf, len);
+
+       kfree(tmpbuf);
+       return retval;
 }
 
 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.