3416 lines · 80.0 KB · C
RawDownload
	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 static void proc_put_char(void **buf, size_t *size, char c){	if (*size) {		char **buffer = (char **)buf;		**buffer = c; 		(*size)--;		(*buffer)++;		*buf = *buffer;	}} static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,				 int *valp,				 int write, void *data){	if (write) {		if (*negp) {			if (*lvalp > (unsigned long) INT_MAX + 1)				return -EINVAL;			*valp = -*lvalp;		} else {			if (*lvalp > (unsigned long) INT_MAX)				return -EINVAL;			*valp = *lvalp;		}	} else {		int val = *valp;		if (val < 0) {			*negp = true;			*lvalp = -(unsigned long)val;		} else {			*negp = false;			*lvalp = (unsigned long)val;		}	}	return 0;} static int do_proc_douintvec_conv(unsigned long *lvalp,				  unsigned int *valp,				  int write, void *data){	if (write) {		if (*lvalp > UINT_MAX)			return -EINVAL;		*valp = *lvalp;	} else {		unsigned int val = *valp;		*lvalp = (unsigned long)val;	}	return 0;} static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,		  int write, void *buffer,		  size_t *lenp, loff_t *ppos,		  int (*conv)(bool *negp, unsigned long *lvalp, int *valp,			      int write, void *data),		  void *data){	int *i, vleft, first = 1, err = 0;	size_t left;	char *p;		if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {		*lenp = 0;		return 0;	}		i = (int *) tbl_data;	vleft = table->maxlen / sizeof(*i);	left = *lenp; 	if (!conv)		conv = do_proc_dointvec_conv; 	if (write) {		if (proc_first_pos_non_zero_ignore(ppos, table))			goto out; 		if (left > PAGE_SIZE - 1)			left = PAGE_SIZE - 1;		p = buffer;	} 	for (; left && vleft--; i++, first=0) {		unsigned long lval;		bool neg; 		if (write) {			left -= proc_skip_spaces(&p); 			if (!left)				break;			err = proc_get_long(&p, &left, &lval, &neg,					     proc_wspace_sep,					     sizeof(proc_wspace_sep), NULL);			if (err)				break;			if (conv(&neg, &lval, i, 1, data)) {				err = -EINVAL;				break;			}		} else {			if (conv(&neg, &lval, i, 0, data)) {				err = -EINVAL;				break;			}			if (!first)				proc_put_char(&buffer, &left, '\t');			proc_put_long(&buffer, &left, lval, neg);		}	} 	if (!write && !first && left && !err)		proc_put_char(&buffer, &left, '\n');	if (write && !err && left)		left -= proc_skip_spaces(&p);	if (write && first)		return err ? : -EINVAL;	*lenp -= left;out:	*ppos += *lenp;	return err;} static int do_proc_dointvec(struct ctl_table *table, int write,		  void *buffer, size_t *lenp, loff_t *ppos,		  int (*conv)(bool *negp, unsigned long *lvalp, int *valp,			      int write, void *data),		  void *data){	return __do_proc_dointvec(table->data, table, write,			buffer, lenp, ppos, conv, data);} static int do_proc_douintvec_w(unsigned int *tbl_data,			       struct ctl_table *table,			       void *buffer,			       size_t *lenp, loff_t *ppos,			       int (*conv)(unsigned long *lvalp,					   unsigned int *valp,					   int write, void *data),			       void *data){	unsigned long lval;	int err = 0;	size_t left;	bool neg;	char *p = buffer; 	left = *lenp; 	if (proc_first_pos_non_zero_ignore(ppos, table))		goto bail_early; 	if (left > PAGE_SIZE - 1)		left = PAGE_SIZE - 1; 	left -= proc_skip_spaces(&p);	if (!left) {		err = -EINVAL;		goto out_free;	} 	err = proc_get_long(&p, &left, &lval, &neg,			     proc_wspace_sep,			     sizeof(proc_wspace_sep), NULL);	if (err || neg) {		err = -EINVAL;		goto out_free;	} 	if (conv(&lval, tbl_data, 1, data)) {		err = -EINVAL;		goto out_free;	} 	if (!err && left)		left -= proc_skip_spaces(&p); out_free:	if (err)		return -EINVAL; 	return 0; 	/* This is in keeping with old __do_proc_dointvec() */bail_early:	*ppos += *lenp;	return err;} static int do_proc_douintvec_r(unsigned int *tbl_data, void *buffer,			       size_t *lenp, loff_t *ppos,			       int (*conv)(unsigned long *lvalp,					   unsigned int *valp,					   int write, void *data),			       void *data){	unsigned long lval;	int err = 0;	size_t left; 	left = *lenp; 	if (conv(&lval, tbl_data, 0, data)) {		err = -EINVAL;		goto out;	} 	proc_put_long(&buffer, &left, lval, false);	if (!left)		goto out; 	proc_put_char(&buffer, &left, '\n'); out:	*lenp -= left;	*ppos += *lenp; 	return err;} static int __do_proc_douintvec(void *tbl_data, struct ctl_table *table,			       int write, void *buffer,			       size_t *lenp, loff_t *ppos,			       int (*conv)(unsigned long *lvalp,					   unsigned int *valp,					   int write, void *data),			       void *data){	unsigned int *i, vleft; 	if (!tbl_data || !table->maxlen || !*lenp || (*ppos && !write)) {		*lenp = 0;		return 0;	} 	i = (unsigned int *) tbl_data;	vleft = table->maxlen / sizeof(*i); 	/*	 * Arrays are not supported, keep this simple. *Do not* add	 * support for them.	 */	if (vleft != 1) {		*lenp = 0;		return -EINVAL;	} 	if (!conv)		conv = do_proc_douintvec_conv; 	if (write)		return do_proc_douintvec_w(i, table, buffer, lenp, ppos,					   conv, data);	return do_proc_douintvec_r(i, buffer, lenp, ppos, conv, data);} static int do_proc_douintvec(struct ctl_table *table, int write,			     void *buffer, size_t *lenp, loff_t *ppos,			     int (*conv)(unsigned long *lvalp,					 unsigned int *valp,					 int write, void *data),			     void *data){	return __do_proc_douintvec(table->data, table, write,				   buffer, lenp, ppos, conv, data);} /** * 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(struct ctl_table *table, int write, void *buffer,		  size_t *lenp, loff_t *ppos){	return do_proc_dointvec(table, write, buffer, lenp, ppos, NULL, NULL);} #ifdef CONFIG_COMPACTIONstatic int proc_dointvec_minmax_warn_RT_change(struct ctl_table *table,		int write, void *buffer, size_t *lenp, loff_t *ppos){	int ret, old; 	if (!IS_ENABLED(CONFIG_PREEMPT_RT) || !write)		return proc_dointvec_minmax(table, write, buffer, lenp, ppos); 	old = *(int *)table->data;	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);	if (ret)		return ret;	if (old != *(int *)table->data)		pr_warn_once("sysctl attribute %s changed by %s[%d]\n",			     table->procname, current->comm,			     task_pid_nr(current));	return ret;}#endif /** * 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(struct 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);} /* * Taint values can only be increased * This means we can safely use a temporary. */static int proc_taint(struct ctl_table *table, int write,			       void *buffer, size_t *lenp, loff_t *ppos){	struct ctl_table t;	unsigned long tmptaint = get_taint();	int err; 	if (write && !capable(CAP_SYS_ADMIN))		return -EPERM; 	t = *table;	t.data = &tmptaint;	err = proc_doulongvec_minmax(&t, write, buffer, lenp, ppos);	if (err < 0)		return err; 	if (write) {		int i; 		/*		 * If we are relying on panic_on_taint not producing		 * false positives due to userspace input, bail out		 * before setting the requested taint flags.		 */		if (panic_on_taint_nousertaint && (tmptaint & panic_on_taint))			return -EINVAL; 		/*		 * Poor man's atomic or. Not worth adding a primitive		 * to everyone's atomic.h for this		 */		for (i = 0; i < TAINT_FLAGS_COUNT; i++)			if ((1UL << i) & tmptaint)				add_taint(i, LOCKDEP_STILL_OK);	} 	return err;} #ifdef CONFIG_PRINTKstatic int proc_dointvec_minmax_sysadmin(struct ctl_table *table, int write,				void *buffer, size_t *lenp, loff_t *ppos){	if (write && !capable(CAP_SYS_ADMIN))		return -EPERM; 	return proc_dointvec_minmax(table, write, buffer, lenp, ppos);}#endif /** * 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;}; static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *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;		*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(struct 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, &param);} /** * 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 {	unsigned int *min;	unsigned int *max;}; static int do_proc_douintvec_minmax_conv(unsigned long *lvalp,					 unsigned int *valp,					 int write, void *data){	int ret;	unsigned int tmp;	struct do_proc_douintvec_minmax_conv_param *param = data;	/* write via temporary local uint for bounds-checking */	unsigned int *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;

Lines 501–1000 of 3416. Previous lines