ASoC: fsl_asrc: Don't access members of config before checking it
[sfrench/cifs-2.6.git] / kernel / sysctl.c
index 40ce2d983b125df86807942093e1c35106f1ed17..ba9ed453c4ed497d62537b019b153fbf47171084 100644 (file)
@@ -173,6 +173,13 @@ extern int no_unaligned_warning;
 #endif
 
 #ifdef CONFIG_PROC_SYSCTL
+
+#define SYSCTL_WRITES_LEGACY   -1
+#define SYSCTL_WRITES_WARN      0
+#define SYSCTL_WRITES_STRICT    1
+
+static int sysctl_writes_strict = SYSCTL_WRITES_WARN;
+
 static int proc_do_cad_pid(struct ctl_table *table, int write,
                  void __user *buffer, size_t *lenp, loff_t *ppos);
 static int proc_taint(struct ctl_table *table, int write,
@@ -195,7 +202,7 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
 /* Note: sysrq code uses it's own private copy */
 static int __sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE;
 
-static int sysrq_sysctl_handler(ctl_table *table, int write,
+static int sysrq_sysctl_handler(struct ctl_table *table, int write,
                                void __user *buffer, size_t *lenp,
                                loff_t *ppos)
 {
@@ -495,6 +502,15 @@ static struct ctl_table kern_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_taint,
        },
+       {
+               .procname       = "sysctl_writes_strict",
+               .data           = &sysctl_writes_strict,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &neg_one,
+               .extra2         = &one,
+       },
 #endif
 #ifdef CONFIG_LATENCYTOP
        {
@@ -1703,8 +1719,8 @@ int __init sysctl_init(void)
 
 #ifdef CONFIG_PROC_SYSCTL
 
-static int _proc_do_string(void* data, int maxlen, int write,
-                          void __user *buffer,
+static int _proc_do_string(char *data, int maxlen, int write,
+                          char __user *buffer,
                           size_t *lenp, loff_t *ppos)
 {
        size_t len;
@@ -1717,21 +1733,30 @@ static int _proc_do_string(void* data, int maxlen, int write,
        }
 
        if (write) {
-               len = 0;
+               if (sysctl_writes_strict == SYSCTL_WRITES_STRICT) {
+                       /* Only continue writes not past the end of buffer. */
+                       len = strlen(data);
+                       if (len > maxlen - 1)
+                               len = maxlen - 1;
+
+                       if (*ppos > len)
+                               return 0;
+                       len = *ppos;
+               } else {
+                       /* Start writing from beginning of buffer. */
+                       len = 0;
+               }
+
+               *ppos += *lenp;
                p = buffer;
-               while (len < *lenp) {
+               while ((p - buffer) < *lenp && len < maxlen - 1) {
                        if (get_user(c, p++))
                                return -EFAULT;
                        if (c == 0 || c == '\n')
                                break;
-                       len++;
+                       data[len++] = c;
                }
-               if (len >= maxlen)
-                       len = maxlen-1;
-               if(copy_from_user(data, buffer, len))
-                       return -EFAULT;
-               ((char *) data)[len] = 0;
-               *ppos += *lenp;
+               data[len] = 0;
        } else {
                len = strlen(data);
                if (len > maxlen)
@@ -1748,10 +1773,10 @@ static int _proc_do_string(void* data, int maxlen, int write,
                if (len > *lenp)
                        len = *lenp;
                if (len)
-                       if(copy_to_user(buffer, data, len))
+                       if (copy_to_user(buffer, data, len))
                                return -EFAULT;
                if (len < *lenp) {
-                       if(put_user('\n', ((char __user *) buffer) + len))
+                       if (put_user('\n', buffer + len))
                                return -EFAULT;
                        len++;
                }
@@ -1761,6 +1786,14 @@ static int _proc_do_string(void* data, int maxlen, int write,
        return 0;
 }
 
+static void warn_sysctl_write(struct ctl_table *table)
+{
+       pr_warn_once("%s wrote to %s when file position was not 0!\n"
+               "This will not be supported in the future. To silence this\n"
+               "warning, set kernel.sysctl_writes_strict = -1\n",
+               current->comm, table->procname);
+}
+
 /**
  * proc_dostring - read a string sysctl
  * @table: the sysctl table
@@ -1781,8 +1814,11 @@ static int _proc_do_string(void* data, int maxlen, int write,
 int proc_dostring(struct ctl_table *table, int write,
                  void __user *buffer, size_t *lenp, loff_t *ppos)
 {
-       return _proc_do_string(table->data, table->maxlen, write,
-                              buffer, lenp, ppos);
+       if (write && *ppos && sysctl_writes_strict == SYSCTL_WRITES_WARN)
+               warn_sysctl_write(table);
+
+       return _proc_do_string((char *)(table->data), table->maxlen, write,
+                              (char __user *)buffer, lenp, ppos);
 }
 
 static size_t proc_skip_spaces(char **buf)
@@ -1956,6 +1992,18 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
                conv = do_proc_dointvec_conv;
 
        if (write) {
+               if (*ppos) {
+                       switch (sysctl_writes_strict) {
+                       case SYSCTL_WRITES_STRICT:
+                               goto out;
+                       case SYSCTL_WRITES_WARN:
+                               warn_sysctl_write(table);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
                if (left > PAGE_SIZE - 1)
                        left = PAGE_SIZE - 1;
                page = __get_free_page(GFP_TEMPORARY);
@@ -2013,6 +2061,7 @@ free:
                        return err ? : -EINVAL;
        }
        *lenp -= left;
+out:
        *ppos += *lenp;
        return err;
 }
@@ -2205,6 +2254,18 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int
        left = *lenp;
 
        if (write) {
+               if (*ppos) {
+                       switch (sysctl_writes_strict) {
+                       case SYSCTL_WRITES_STRICT:
+                               goto out;
+                       case SYSCTL_WRITES_WARN:
+                               warn_sysctl_write(table);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
                if (left > PAGE_SIZE - 1)
                        left = PAGE_SIZE - 1;
                page = __get_free_page(GFP_TEMPORARY);
@@ -2260,6 +2321,7 @@ free:
                        return err ? : -EINVAL;
        }
        *lenp -= left;
+out:
        *ppos += *lenp;
        return err;
 }
@@ -2506,11 +2568,11 @@ int proc_do_large_bitmap(struct ctl_table *table, int write,
        bool first = 1;
        size_t left = *lenp;
        unsigned long bitmap_len = table->maxlen;
-       unsigned long *bitmap = (unsigned long *) table->data;
+       unsigned long *bitmap = *(unsigned long **) table->data;
        unsigned long *tmp_bitmap = NULL;
        char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c;
 
-       if (!bitmap_len || !left || (*ppos && !write)) {
+       if (!bitmap || !bitmap_len || !left || (*ppos && !write)) {
                *lenp = 0;
                return 0;
        }