/* Constants used for minimum and maximum */ staticconstint ngroups_max = NGROUPS_MAX; staticconstint cap_last_cap = CAP_LAST_CAP;
#ifdef CONFIG_PROC_SYSCTL
/** * enum sysctl_writes_mode - supported sysctl write modes * * @SYSCTL_WRITES_LEGACY: each write syscall must fully contain the sysctl value * to be written, and multiple writes on the same sysctl file descriptor * will rewrite the sysctl value, regardless of file position. No warning * is issued when the initial position is not 0. * @SYSCTL_WRITES_WARN: same as above but warn when the initial file position is * not 0. * @SYSCTL_WRITES_STRICT: writes to numeric sysctl entries must always be at * file position 0 and the value must be fully contained in the buffer * sent to the write syscall. If dealing with strings respect the file * position, but restrict this to the max length of the buffer, anything * passed the max length will be ignored. Multiple writes will append * to the buffer. * * These write modes control how current file position affects the behavior of * updating sysctl values through the proc interface on each write.
*/ enum sysctl_writes_mode {
SYSCTL_WRITES_LEGACY = -1,
SYSCTL_WRITES_WARN = 0,
SYSCTL_WRITES_STRICT = 1,
};
if (write) { 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 ((p - buffer) < *lenp && len < maxlen - 1) {
c = *(p++); if (c == 0 || c == '\n') break;
data[len++] = c;
}
data[len] = 0;
} else {
len = strlen(data); if (len > maxlen)
len = maxlen;
if (*ppos > len) {
*lenp = 0; return 0;
}
data += *ppos;
len -= *ppos;
if (len > *lenp)
len = *lenp; if (len)
memcpy(buffer, data, len); if (len < *lenp) {
buffer[len] = '\n';
len++;
}
*lenp = len;
*ppos += len;
} return 0;
}
staticvoid warn_sysctl_write(conststruct 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_first_pos_non_zero_ignore - check if first position is allowed * @ppos: file position * @table: the sysctl table * * Returns true if the first position is non-zero and the sysctl_writes_strict * mode indicates this is not allowed for numeric input types. String proc * handlers can ignore the return value.
*/ staticbool proc_first_pos_non_zero_ignore(loff_t *ppos, conststruct ctl_table *table)
{ if (!*ppos) returnfalse;
switch (sysctl_writes_strict) { case SYSCTL_WRITES_STRICT: returntrue; case SYSCTL_WRITES_WARN:
warn_sysctl_write(table); returnfalse; default: returnfalse;
}
}
/** * proc_dostring - read a string sysctl * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes a string from/to the user buffer. If the kernel * buffer provided is not large enough to hold the string, the * string is truncated. The copied string is %NULL-terminated. * If the string is being read by the user process, it is copied * and a newline '\n' is added. It is truncated if the buffer is * not large enough. * * Returns 0 on success.
*/ int proc_dostring(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ if (write)
proc_first_pos_non_zero_ignore(ppos, table);
staticvoid proc_skip_spaces(char **buf, size_t *size)
{ while (*size) { if (!isspace(**buf)) break;
(*size)--;
(*buf)++;
}
}
staticvoid proc_skip_char(char **buf, size_t *size, constchar v)
{ while (*size) { if (**buf != v) break;
(*size)--;
(*buf)++;
}
}
/** * strtoul_lenient - parse an ASCII formatted integer from a buffer and only * fail on overflow * * @cp: kernel buffer containing the string to parse * @endp: pointer to store the trailing characters * @base: the base to use * @res: where the parsed integer will be stored * * In case of success 0 is returned and @res will contain the parsed integer, * @endp will hold any trailing characters. * This function will fail the parse on overflow. If there wasn't an overflow * the function will defer the decision what characters count as invalid to the * caller.
*/ staticint strtoul_lenient(constchar *cp, char **endp, unsignedint base, unsignedlong *res)
{ unsignedlonglong result; unsignedint rv;
#define TMPBUFLEN 22 /** * proc_get_long - reads an ASCII formatted integer from a user buffer * * @buf: a kernel buffer * @size: size of the kernel buffer * @val: this is where the number will be stored * @neg: set to %TRUE if number is negative * @perm_tr: a vector which contains the allowed trailers * @perm_tr_len: size of the perm_tr vector * @tr: pointer to store the trailer character * * In case of success %0 is returned and @buf and @size are updated with * the amount of bytes read. If @tr is non-NULL and a trailing * character exists (size is non-zero after returning from this * function), @tr is updated with the trailing character.
*/ staticint proc_get_long(char **buf, size_t *size, unsignedlong *val, bool *neg, constchar *perm_tr, unsigned perm_tr_len, char *tr)
{ char *p, tmp[TMPBUFLEN];
ssize_t len = *size;
if (len <= 0) return -EINVAL;
if (len > TMPBUFLEN - 1)
len = TMPBUFLEN - 1;
memcpy(tmp, *buf, len);
tmp[len] = 0;
p = tmp; if (*p == '-' && *size > 1) {
*neg = true;
p++;
} else
*neg = false; if (!isdigit(*p)) return -EINVAL;
if (strtoul_lenient(p, &p, 0, val)) return -EINVAL;
len = p - tmp;
/* We don't know if the next char is whitespace thus we may accept * invalid integers (e.g. 1234...a) or two integers instead of one
* (e.g. 123...1). So lets not allow such large numbers. */ if (len == TMPBUFLEN - 1) return -EINVAL;
/** * proc_put_long - converts an integer to a decimal ASCII formatted string * * @buf: the user buffer * @size: the size of the user buffer * @val: the integer to be converted * @neg: sign of the number, %TRUE for negative * * In case of success @buf and @size are updated with the amount of bytes * written.
*/ staticvoid proc_put_long(void **buf, size_t *size, unsignedlong val, bool neg)
{ int len; char tmp[TMPBUFLEN], *p = tmp;
sprintf(p, "%s%lu", neg ? "-" : "", val);
len = strlen(tmp); if (len > *size)
len = *size;
memcpy(*buf, tmp, len);
*size -= len;
*buf += len;
} #undef TMPBUFLEN
staticvoid proc_put_char(void **buf, size_t *size, char c)
{ if (*size) { char **buffer = (char **)buf;
**buffer = c;
(*size)--;
(*buffer)++;
*buf = *buffer;
}
}
staticint do_proc_dointvec_conv(bool *negp, unsignedlong *lvalp, int *valp, int write, void *data)
{ if (write) { if (*negp) { if (*lvalp > (unsignedlong) INT_MAX + 1) return -EINVAL;
WRITE_ONCE(*valp, -*lvalp);
} else { if (*lvalp > (unsignedlong) INT_MAX) return -EINVAL;
WRITE_ONCE(*valp, *lvalp);
}
} else { int val = READ_ONCE(*valp); if (val < 0) {
*negp = true;
*lvalp = -(unsignedlong)val;
} else {
*negp = false;
*lvalp = (unsignedlong)val;
}
} return 0;
}
staticint do_proc_douintvec_conv(unsignedlong *lvalp, unsignedint *valp, int write, void *data)
{ if (write) { if (*lvalp > UINT_MAX) return -EINVAL;
WRITE_ONCE(*valp, *lvalp);
} else { unsignedint val = READ_ONCE(*valp);
*lvalp = (unsignedlong)val;
} return 0;
}
int do_proc_douintvec(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos, int (*conv)(unsignedlong *lvalp, unsignedint *valp, int write, void *data), void *data)
{ return __do_proc_douintvec(table->data, table, write,
buffer, lenp, ppos, conv, data);
}
/** * proc_dobool - read/write a bool * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes one integer value from/to the user buffer, * treated as an ASCII string. * * table->data must point to a bool variable and table->maxlen must * be sizeof(bool). * * Returns 0 on success.
*/ int proc_dobool(conststruct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos)
{ struct ctl_table tmp; bool *data = table->data; int res, val;
/* Do not support arrays yet. */ if (table->maxlen != sizeof(bool)) return -EINVAL;
val = READ_ONCE(*data);
res = proc_dointvec(&tmp, write, buffer, lenp, ppos); if (res) return res; if (write)
WRITE_ONCE(*data, val); return 0;
}
/** * proc_dointvec - read a vector of integers * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * * Returns 0 on success.
*/ int proc_dointvec(conststruct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos)
{ return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL, NULL);
}
/** * proc_douintvec - read a vector of unsigned integers * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer * values from/to the user buffer, treated as an ASCII string. * * Returns 0 on success.
*/ int proc_douintvec(conststruct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos)
{ return do_proc_douintvec(table, write, buffer, lenp, ppos,
do_proc_douintvec_conv, NULL);
}
/** * struct do_proc_dointvec_minmax_conv_param - proc_dointvec_minmax() range checking structure * @min: pointer to minimum allowable value * @max: pointer to maximum allowable value * * The do_proc_dointvec_minmax_conv_param structure provides the * minimum and maximum values for doing range checking for those sysctl * parameters that use the proc_dointvec_minmax() handler.
*/ struct do_proc_dointvec_minmax_conv_param { int *min; int *max;
};
staticint do_proc_dointvec_minmax_conv(bool *negp, unsignedlong *lvalp, int *valp, int write, void *data)
{ int tmp, ret; struct do_proc_dointvec_minmax_conv_param *param = data; /* * If writing, first do so via a temporary local int so we can * bounds-check it before touching *valp.
*/ int *ip = write ? &tmp : valp;
ret = do_proc_dointvec_conv(negp, lvalp, ip, write, data); if (ret) return ret;
if (write) { if ((param->min && *param->min > tmp) ||
(param->max && *param->max < tmp)) return -EINVAL;
WRITE_ONCE(*valp, tmp);
}
return 0;
}
/** * proc_dointvec_minmax - read a vector of integers with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * * This routine will ensure the values are within the range specified by * table->extra1 (min) and table->extra2 (max). * * Returns 0 on success or -EINVAL on write when the range check fails.
*/ int proc_dointvec_minmax(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ struct do_proc_dointvec_minmax_conv_param param = {
.min = (int *) table->extra1,
.max = (int *) table->extra2,
}; return do_proc_dointvec(table, write, buffer, lenp, ppos,
do_proc_dointvec_minmax_conv, ¶m);
}
/** * struct do_proc_douintvec_minmax_conv_param - proc_douintvec_minmax() range checking structure * @min: pointer to minimum allowable value * @max: pointer to maximum allowable value * * The do_proc_douintvec_minmax_conv_param structure provides the * minimum and maximum values for doing range checking for those sysctl * parameters that use the proc_douintvec_minmax() handler.
*/ struct do_proc_douintvec_minmax_conv_param { unsignedint *min; unsignedint *max;
};
staticint do_proc_douintvec_minmax_conv(unsignedlong *lvalp, unsignedint *valp, int write, void *data)
{ int ret; unsignedint tmp; struct do_proc_douintvec_minmax_conv_param *param = data; /* write via temporary local uint for bounds-checking */ unsignedint *up = write ? &tmp : valp;
ret = do_proc_douintvec_conv(lvalp, up, write, data); if (ret) return ret;
if (write) { if ((param->min && *param->min > tmp) ||
(param->max && *param->max < tmp)) return -ERANGE;
WRITE_ONCE(*valp, tmp);
}
return 0;
}
/** * proc_douintvec_minmax - read a vector of unsigned ints with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer * values from/to the user buffer, treated as an ASCII string. Negative * strings are not allowed. * * This routine will ensure the values are within the range specified by * table->extra1 (min) and table->extra2 (max). There is a final sanity * check for UINT_MAX to avoid having to support wrap around uses from * userspace. * * Returns 0 on success or -ERANGE on write when the range check fails.
*/ int proc_douintvec_minmax(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ struct do_proc_douintvec_minmax_conv_param param = {
.min = (unsignedint *) table->extra1,
.max = (unsignedint *) table->extra2,
}; return do_proc_douintvec(table, write, buffer, lenp, ppos,
do_proc_douintvec_minmax_conv, ¶m);
}
/** * proc_dou8vec_minmax - read a vector of unsigned chars with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(u8) unsigned chars * values from/to the user buffer, treated as an ASCII string. Negative * strings are not allowed. * * This routine will ensure the values are within the range specified by * table->extra1 (min) and table->extra2 (max). * * Returns 0 on success or an error on write when the range check fails.
*/ int proc_dou8vec_minmax(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ struct ctl_table tmp; unsignedint min = 0, max = 255U, val;
u8 *data = table->data; struct do_proc_douintvec_minmax_conv_param param = {
.min = &min,
.max = &max,
}; int res;
/* Do not support arrays yet. */ if (table->maxlen != sizeof(u8)) return -EINVAL;
if (table->extra1)
min = *(unsignedint *) table->extra1; if (table->extra2)
max = *(unsignedint *) table->extra2;
tmp = *table;
tmp.maxlen = sizeof(val);
tmp.data = &val;
val = READ_ONCE(*data);
res = do_proc_douintvec(&tmp, write, buffer, lenp, ppos,
do_proc_douintvec_minmax_conv, ¶m); if (res) return res; if (write)
WRITE_ONCE(*data, val); return 0;
}
EXPORT_SYMBOL_GPL(proc_dou8vec_minmax);
/** * proc_doulongvec_minmax - read a vector of long integers with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned long) unsigned long * values from/to the user buffer, treated as an ASCII string. * * This routine will ensure the values are within the range specified by * table->extra1 (min) and table->extra2 (max). * * Returns 0 on success.
*/ int proc_doulongvec_minmax(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ return do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos, 1l, 1l);
}
/** * proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned long) unsigned long * values from/to the user buffer, treated as an ASCII string. The values * are treated as milliseconds, and converted to jiffies when they are stored. * * This routine will ensure the values are within the range specified by * table->extra1 (min) and table->extra2 (max). * * Returns 0 on success.
*/ int proc_doulongvec_ms_jiffies_minmax(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ return do_proc_doulongvec_minmax(table, write, buffer,
lenp, ppos, HZ, 1000l);
}
staticint do_proc_dointvec_jiffies_conv(bool *negp, unsignedlong *lvalp, int *valp, int write, void *data)
{ if (write) { if (*lvalp > INT_MAX / HZ) return 1; if (*negp)
WRITE_ONCE(*valp, -*lvalp * HZ); else
WRITE_ONCE(*valp, *lvalp * HZ);
} else { int val = READ_ONCE(*valp); unsignedlong lval; if (val < 0) {
*negp = true;
lval = -(unsignedlong)val;
} else {
*negp = false;
lval = (unsignedlong)val;
}
*lvalp = lval / HZ;
} return 0;
}
staticint do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsignedlong *lvalp, int *valp, int write, void *data)
{ int tmp, ret; struct do_proc_dointvec_minmax_conv_param *param = data; /* * If writing, first do so via a temporary local int so we can * bounds-check it before touching *valp.
*/ int *ip = write ? &tmp : valp;
ret = do_proc_dointvec_ms_jiffies_conv(negp, lvalp, ip, write, data); if (ret) return ret;
/** * proc_dointvec_jiffies - read a vector of integers as seconds * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * The values read are assumed to be in seconds, and are converted into * jiffies. * * Returns 0 on success.
*/ int proc_dointvec_jiffies(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ return do_proc_dointvec(table,write,buffer,lenp,ppos,
do_proc_dointvec_jiffies_conv,NULL);
}
/** * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: pointer to the file position * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * The values read are assumed to be in 1/USER_HZ seconds, and * are converted into jiffies. * * Returns 0 on success.
*/ int proc_dointvec_userhz_jiffies(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ return do_proc_dointvec(table, write, buffer, lenp, ppos,
do_proc_dointvec_userhz_jiffies_conv, NULL);
}
/** * proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: the current position in the file * * Reads/writes up to table->maxlen/sizeof(unsigned int) integer * values from/to the user buffer, treated as an ASCII string. * The values read are assumed to be in 1/1000 seconds, and * are converted into jiffies. * * Returns 0 on success.
*/ int proc_dointvec_ms_jiffies(conststruct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos)
{ return do_proc_dointvec(table, write, buffer, lenp, ppos,
do_proc_dointvec_ms_jiffies_conv, NULL);
}
/** * proc_do_large_bitmap - read/write from/to a large bitmap * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position * * The bitmap is stored at table->data and the bitmap length (in bits) * in table->maxlen. * * We use a range comma separated format (e.g. 1,3-4,10-10) so that * large bitmaps may be represented in a compact manner. Writing into * the file will clear the bitmap then update it with the given input. * * Returns 0 on success.
*/ int proc_do_large_bitmap(conststruct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos)
{ int err = 0;
size_t left = *lenp; unsignedlong bitmap_len = table->maxlen; unsignedlong *bitmap = *(unsignedlong **) table->data; unsignedlong *tmp_bitmap = NULL; char tr_a[] = { '-', ',', '\n' }, tr_b[] = { ',', '\n', 0 }, c;
/* In case we stop parsing mid-number, we can reset */
saved_left = left;
err = proc_get_long(&p, &left, &val_a, &neg, tr_a, sizeof(tr_a), &c); /* * If we consumed the entirety of a truncated buffer or * only one char is left (may be a "-"), then stop here, * reset, & come back for more.
*/ if ((left <= 1) && skipped) {
left = saved_left; break;
}
if (err) break; if (val_a >= bitmap_len || neg) {
err = -EINVAL; break;
}
val_b = val_a; if (left) {
p++;
left--;
}
if (c == '-') {
err = proc_get_long(&p, &left, &val_b,
&neg, tr_b, sizeof(tr_b),
&c); /* * If we consumed all of a truncated buffer or * then stop here, reset, & come back for more.
*/ if (!left && skipped) {
left = saved_left; break;
}
if (err) break; if (val_b >= bitmap_len || neg ||
val_a > val_b) {
err = -EINVAL; break;
} if (left) {
p++;
left--;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.