diff options
Diffstat (limited to 'kernel/sysctl.c')
| -rw-r--r-- | kernel/sysctl.c | 390 |
1 files changed, 229 insertions, 161 deletions
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 8686b0f5fc12..4a976208de29 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
| @@ -2040,8 +2040,122 @@ int proc_dostring(struct ctl_table *table, int write, | |||
| 2040 | buffer, lenp, ppos); | 2040 | buffer, lenp, ppos); |
| 2041 | } | 2041 | } |
| 2042 | 2042 | ||
| 2043 | static size_t proc_skip_spaces(char **buf) | ||
| 2044 | { | ||
| 2045 | size_t ret; | ||
| 2046 | char *tmp = skip_spaces(*buf); | ||
| 2047 | ret = tmp - *buf; | ||
| 2048 | *buf = tmp; | ||
| 2049 | return ret; | ||
| 2050 | } | ||
| 2051 | |||
| 2052 | #define TMPBUFLEN 22 | ||
| 2053 | /** | ||
| 2054 | * proc_get_long - reads an ASCII formated integer from a user buffer | ||
| 2055 | * | ||
| 2056 | * @buf - a kernel buffer | ||
| 2057 | * @size - size of the kernel buffer | ||
| 2058 | * @val - this is where the number will be stored | ||
| 2059 | * @neg - set to %TRUE if number is negative | ||
| 2060 | * @perm_tr - a vector which contains the allowed trailers | ||
| 2061 | * @perm_tr_len - size of the perm_tr vector | ||
| 2062 | * @tr - pointer to store the trailer character | ||
| 2063 | * | ||
| 2064 | * In case of success 0 is returned and buf and size are updated with | ||
| 2065 | * the amount of bytes read. If tr is non NULL and a trailing | ||
| 2066 | * character exist (size is non zero after returning from this | ||
| 2067 | * function) tr is updated with the trailing character. | ||
| 2068 | */ | ||
| 2069 | static int proc_get_long(char **buf, size_t *size, | ||
| 2070 | unsigned long *val, bool *neg, | ||
| 2071 | const char *perm_tr, unsigned perm_tr_len, char *tr) | ||
| 2072 | { | ||
| 2073 | int len; | ||
| 2074 | char *p, tmp[TMPBUFLEN]; | ||
| 2075 | |||
| 2076 | if (!*size) | ||
| 2077 | return -EINVAL; | ||
| 2078 | |||
| 2079 | len = *size; | ||
| 2080 | if (len > TMPBUFLEN - 1) | ||
| 2081 | len = TMPBUFLEN - 1; | ||
| 2082 | |||
| 2083 | memcpy(tmp, *buf, len); | ||
| 2084 | |||
| 2085 | tmp[len] = 0; | ||
| 2086 | p = tmp; | ||
| 2087 | if (*p == '-' && *size > 1) { | ||
| 2088 | *neg = true; | ||
| 2089 | p++; | ||
| 2090 | } else | ||
| 2091 | *neg = false; | ||
| 2092 | if (!isdigit(*p)) | ||
| 2093 | return -EINVAL; | ||
| 2094 | |||
| 2095 | *val = simple_strtoul(p, &p, 0); | ||
| 2096 | |||
| 2097 | len = p - tmp; | ||
| 2098 | |||
| 2099 | /* We don't know if the next char is whitespace thus we may accept | ||
| 2100 | * invalid integers (e.g. 1234...a) or two integers instead of one | ||
| 2101 | * (e.g. 123...1). So lets not allow such large numbers. */ | ||
| 2102 | if (len == TMPBUFLEN - 1) | ||
| 2103 | return -EINVAL; | ||
| 2104 | |||
| 2105 | if (len < *size && perm_tr_len && !memchr(perm_tr, *p, perm_tr_len)) | ||
| 2106 | return -EINVAL; | ||
| 2043 | 2107 | ||
| 2044 | static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, | 2108 | if (tr && (len < *size)) |
| 2109 | *tr = *p; | ||
| 2110 | |||
| 2111 | *buf += len; | ||
| 2112 | *size -= len; | ||
| 2113 | |||
| 2114 | return 0; | ||
| 2115 | } | ||
| 2116 | |||
| 2117 | /** | ||
| 2118 | * proc_put_long - coverts an integer to a decimal ASCII formated string | ||
| 2119 | * | ||
| 2120 | * @buf - the user buffer | ||
| 2121 | * @size - the size of the user buffer | ||
| 2122 | * @val - the integer to be converted | ||
| 2123 | * @neg - sign of the number, %TRUE for negative | ||
| 2124 | * | ||
| 2125 | * In case of success 0 is returned and buf and size are updated with | ||
| 2126 | * the amount of bytes read. | ||
| 2127 | */ | ||
| 2128 | static int proc_put_long(void __user **buf, size_t *size, unsigned long val, | ||
| 2129 | bool neg) | ||
| 2130 | { | ||
| 2131 | int len; | ||
| 2132 | char tmp[TMPBUFLEN], *p = tmp; | ||
| 2133 | |||
| 2134 | sprintf(p, "%s%lu", neg ? "-" : "", val); | ||
| 2135 | len = strlen(tmp); | ||
| 2136 | if (len > *size) | ||
| 2137 | len = *size; | ||
| 2138 | if (copy_to_user(*buf, tmp, len)) | ||
| 2139 | return -EFAULT; | ||
| 2140 | *size -= len; | ||
| 2141 | *buf += len; | ||
| 2142 | return 0; | ||
| 2143 | } | ||
| 2144 | #undef TMPBUFLEN | ||
| 2145 | |||
| 2146 | static int proc_put_char(void __user **buf, size_t *size, char c) | ||
| 2147 | { | ||
| 2148 | if (*size) { | ||
| 2149 | char __user **buffer = (char __user **)buf; | ||
| 2150 | if (put_user(c, *buffer)) | ||
| 2151 | return -EFAULT; | ||
| 2152 | (*size)--, (*buffer)++; | ||
| 2153 | *buf = *buffer; | ||
| 2154 | } | ||
| 2155 | return 0; | ||
| 2156 | } | ||
| 2157 | |||
| 2158 | static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, | ||
| 2045 | int *valp, | 2159 | int *valp, |
| 2046 | int write, void *data) | 2160 | int write, void *data) |
| 2047 | { | 2161 | { |
| @@ -2050,33 +2164,31 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, | |||
| 2050 | } else { | 2164 | } else { |
| 2051 | int val = *valp; | 2165 | int val = *valp; |
| 2052 | if (val < 0) { | 2166 | if (val < 0) { |
| 2053 | *negp = -1; | 2167 | *negp = true; |
| 2054 | *lvalp = (unsigned long)-val; | 2168 | *lvalp = (unsigned long)-val; |
| 2055 | } else { | 2169 | } else { |
| 2056 | *negp = 0; | 2170 | *negp = false; |
| 2057 | *lvalp = (unsigned long)val; | 2171 | *lvalp = (unsigned long)val; |
| 2058 | } | 2172 | } |
| 2059 | } | 2173 | } |
| 2060 | return 0; | 2174 | return 0; |
| 2061 | } | 2175 | } |
| 2062 | 2176 | ||
| 2177 | static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; | ||
| 2178 | |||
| 2063 | static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, | 2179 | static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, |
| 2064 | int write, void __user *buffer, | 2180 | int write, void __user *buffer, |
| 2065 | size_t *lenp, loff_t *ppos, | 2181 | size_t *lenp, loff_t *ppos, |
| 2066 | int (*conv)(int *negp, unsigned long *lvalp, int *valp, | 2182 | int (*conv)(bool *negp, unsigned long *lvalp, int *valp, |
| 2067 | int write, void *data), | 2183 | int write, void *data), |
| 2068 | void *data) | 2184 | void *data) |
| 2069 | { | 2185 | { |
| 2070 | #define TMPBUFLEN 21 | 2186 | int *i, vleft, first = 1, err = 0; |
| 2071 | int *i, vleft, first = 1, neg; | 2187 | unsigned long page = 0; |
| 2072 | unsigned long lval; | 2188 | size_t left; |
| 2073 | size_t left, len; | 2189 | char *kbuf; |
| 2074 | |||
| 2075 | char buf[TMPBUFLEN], *p; | ||
| 2076 | char __user *s = buffer; | ||
| 2077 | 2190 | ||
| 2078 | if (!tbl_data || !table->maxlen || !*lenp || | 2191 | if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) { |
| 2079 | (*ppos && !write)) { | ||
| 2080 | *lenp = 0; | 2192 | *lenp = 0; |
| 2081 | return 0; | 2193 | return 0; |
| 2082 | } | 2194 | } |
| @@ -2088,89 +2200,69 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, | |||
| 2088 | if (!conv) | 2200 | if (!conv) |
| 2089 | conv = do_proc_dointvec_conv; | 2201 | conv = do_proc_dointvec_conv; |
| 2090 | 2202 | ||
| 2203 | if (write) { | ||
| 2204 | if (left > PAGE_SIZE - 1) | ||
| 2205 | left = PAGE_SIZE - 1; | ||
| 2206 | page = __get_free_page(GFP_TEMPORARY); | ||
| 2207 | kbuf = (char *) page; | ||
| 2208 | if (!kbuf) | ||
| 2209 | return -ENOMEM; | ||
| 2210 | if (copy_from_user(kbuf, buffer, left)) { | ||
| 2211 | err = -EFAULT; | ||
| 2212 | goto free; | ||
| 2213 | } | ||
| 2214 | kbuf[left] = 0; | ||
| 2215 | } | ||
| 2216 | |||
| 2091 | for (; left && vleft--; i++, first=0) { | 2217 | for (; left && vleft--; i++, first=0) { |
| 2092 | if (write) { | 2218 | unsigned long lval; |
| 2093 | while (left) { | 2219 | bool neg; |
| 2094 | char c; | ||
| 2095 | if (get_user(c, s)) | ||
| 2096 | return -EFAULT; | ||
| 2097 | if (!isspace(c)) | ||
| 2098 | break; | ||
| 2099 | left--; | ||
