[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |