From patchwork Sun Feb 9 15:58:09 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wen Yang X-Patchwork-Id: 13966992 Received: from out-172.mta1.migadu.com (out-172.mta1.migadu.com [95.215.58.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C46751DF960 for ; Sun, 9 Feb 2025 15:59:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116767; cv=none; b=qk83m/OZ2JFCT5wkv4xoOK7GDad+9P5cqmgGy4EQyOHENuGYJMbhn0qvbyxc/lP6OaLK9Hx4lc9sTE5DeRFPevDJAP6cOkLUD29zCZfMqPcS0C06n6mKsIYpDodmWIGgHfMFEMmOmYF7MFKbVTN+X09Ib44OAQBn2HcodZ1Svyo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116767; c=relaxed/simple; bh=931aNdIXPIUpbHsHw8ZK5kFYcUiLD3aCLpR779Guzas=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=aIygbpaDdEl08/Mk223IjqUEO+9bgXgMFhUNcuSe4BJXbGd28GNRh+6rb4ExTh528f+Uqanno9V9oBRYouVHkNeztsw2HbGU5or62JbXZZg4NE1Nsaunx2XbLitLs/z/qAchwC62YgEYST3/DhDvDe7euyzlzN08INKpnY1AS30= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=szwBMJA0; arc=none smtp.client-ip=95.215.58.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="szwBMJA0" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1739116763; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=IflWQqWsaChhlNvqL4gKDpLadjB6EU1tUKNrqGCslwY=; b=szwBMJA03NbnnT/D9UB6YWHs9ueRuo18EU0HTY3/jbDBDWd5XtrNgHeA/6hQmfbdfE5ubJ 7LkNGn+CbCt5QoJq0J24VAQStT8u0dtuKhGkH+s9obx0417IRD1nvwxWqyNupGV/osQMFf sYRkqaKiPPuZaaqfOwXRDFACITqPQiQ= From: Wen Yang To: Joel Granados , Luis Chamberlain , Kees Cook Cc: "Eric W . Biederman" , Dave Young , Christian Brauner , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Wen Yang , Joel Granados Subject: [PATCH v5 1/5] sysctl: simplify the min/max boundary check Date: Sun, 9 Feb 2025 23:58:09 +0800 Message-Id: In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT Eric pointed out: - That is just replace extra1 and extra2 with min and max members. The - members don't have any reason to be pointers. Without being pointers - the min/max functions can just use long values to cap either ints or - longs, and there is no room for error. The integer promotion rules - will ensure that even negative values can be stored in unsigned long - min and max values successfully. Plus it is all bog standard C - so there is nothing special to learn. In order to archive it, we first introduce a new struct do_proc_minmax_conv_param to replace both do_proc_dointvec_minmax_conv_param and do_proc_douintvec_minmax_conv_param, both int *{min, max} and unsigned int *{min, max} are changed to unsigned long {min, max}, both do_proc_dointvec_conv() and do_proc_douintvec_conv() deleted. Selftest were also made: [23:16:38] ================ sysctl_test (11 subtests) ================= [23:16:38] [PASSED] sysctl_test_api_dointvec_null_tbl_data [23:16:38] [PASSED] sysctl_test_api_dointvec_table_maxlen_unset [23:16:38] [PASSED] sysctl_test_api_dointvec_table_len_is_zero [23:16:38] [PASSED] sysctl_test_api_dointvec_table_read_but_position_set [23:16:38] [PASSED] sysctl_test_dointvec_read_happy_single_positive [23:16:38] [PASSED] sysctl_test_dointvec_read_happy_single_negative [23:16:38] [PASSED] sysctl_test_dointvec_write_happy_single_positive [23:16:38] [PASSED] sysctl_test_dointvec_write_happy_single_negative [23:16:38] [PASSED] sysctl_test_api_dointvec_write_single_less_int_min [23:16:38] [PASSED] sysctl_test_api_dointvec_write_single_greater_int_max [23:16:38] [PASSED] sysctl_test_register_sysctl_sz_invalid_extra_value [23:16:38] =================== [PASSED] sysctl_test =================== [23:16:38] ============================================================ [23:16:38] Testing complete. Ran 11 tests: passed: 11 Signed-off-by: Wen Yang Cc: Joel Granados Cc: Luis Chamberlain Cc: Kees Cook Cc: Eric W. Biederman Cc: Christian Brauner Cc: Dave Young Cc: linux-kernel@vger.kernel.org --- kernel/sysctl.c | 199 ++++++++++++++++++------------------------------ 1 file changed, 73 insertions(+), 126 deletions(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 21190098c21d..4507795e3568 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -409,22 +409,44 @@ static void proc_put_char(void **buf, size_t *size, char c) } } -static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, - int *valp, - int write, void *data) +/** + * struct do_proc_minmax_conv_param - proc_dointvec_minmax() range checking structure + * @min: the minimum allowable value + * @max: the maximum allowable value + * + * The do_proc_minmax_conv_param structure provides the + * minimum and maximum values for doing range checking for those sysctl + * parameters that use the proc_dointvec_minmax(), proc_dou8vec_minmax() and so on. + */ +struct do_proc_minmax_conv_param { + unsigned long min; + unsigned long max; +}; + +static inline int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp, + int *valp, + int write, void *data) { + struct do_proc_minmax_conv_param *param = data; + long min, max; + long val; + if (write) { if (*negp) { - if (*lvalp > (unsigned long) INT_MAX + 1) - return -EINVAL; - WRITE_ONCE(*valp, -*lvalp); + max = param ? param->max : 0; + min = param ? param->min : INT_MIN; + val = -*lvalp; } else { - if (*lvalp > (unsigned long) INT_MAX) - return -EINVAL; - WRITE_ONCE(*valp, *lvalp); + max = param ? param->max : INT_MAX; + min = param ? param->min : 0; + val = *lvalp; } + + if (val > max || val < min) + return -EINVAL; + WRITE_ONCE(*valp, (int)val); } else { - int val = READ_ONCE(*valp); + val = (int)READ_ONCE(*valp); if (val < 0) { *negp = true; *lvalp = -(unsigned long)val; @@ -433,21 +455,30 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, *lvalp = (unsigned long)val; } } + return 0; } -static int do_proc_douintvec_conv(unsigned long *lvalp, - unsigned int *valp, - int write, void *data) +static inline int do_proc_douintvec_minmax_conv(unsigned long *lvalp, + unsigned int *valp, + int write, void *data) { + struct do_proc_minmax_conv_param *param = data; + unsigned long min, max; + if (write) { - if (*lvalp > UINT_MAX) + max = param ? param->max : UINT_MAX; + min = param ? param->min : 0; + + if (*lvalp > max || *lvalp < min) return -EINVAL; - WRITE_ONCE(*valp, *lvalp); + + WRITE_ONCE(*valp, (unsigned int)*lvalp); } else { unsigned int val = READ_ONCE(*valp); *lvalp = (unsigned long)val; } + return 0; } @@ -474,7 +505,7 @@ static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table, left = *lenp; if (!conv) - conv = do_proc_dointvec_conv; + conv = do_proc_dointvec_minmax_conv; if (write) { if (proc_first_pos_non_zero_ignore(ppos, table)) @@ -652,7 +683,7 @@ static int __do_proc_douintvec(void *tbl_data, const struct ctl_table *table, } if (!conv) - conv = do_proc_douintvec_conv; + conv = do_proc_douintvec_minmax_conv; if (write) return do_proc_douintvec_w(i, table, buffer, lenp, ppos, @@ -747,7 +778,7 @@ int proc_douintvec(const 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); + do_proc_douintvec_minmax_conv, NULL); } /* @@ -793,46 +824,6 @@ static int proc_taint(const struct ctl_table *table, int write, return err; } -/** - * 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; - WRITE_ONCE(*valp, tmp); - } - - return 0; -} - /** * proc_dointvec_minmax - read a vector of integers with min/max values * @table: the sysctl table @@ -852,53 +843,14 @@ static int do_proc_dointvec_minmax_conv(bool *negp, unsigned long *lvalp, int proc_dointvec_minmax(const 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, - }; + struct do_proc_minmax_conv_param param; + + param.min = (table->extra1) ? *(int *) table->extra1 : INT_MIN; + param.max = (table->extra2) ? *(int *) table->extra2 : INT_MAX; 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 { - 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; - - WRITE_ONCE(*valp, tmp); - } - - return 0; -} - /** * proc_douintvec_minmax - read a vector of unsigned ints with min/max values * @table: the sysctl table @@ -921,10 +873,11 @@ static int do_proc_douintvec_minmax_conv(unsigned long *lvalp, int proc_douintvec_minmax(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { - struct do_proc_douintvec_minmax_conv_param param = { - .min = (unsigned int *) table->extra1, - .max = (unsigned int *) table->extra2, - }; + struct do_proc_minmax_conv_param param; + + param.min = (table->extra1) ? *(unsigned int *) table->extra1 : 0; + param.max = (table->extra2) ? *(unsigned int *) table->extra2 : UINT_MAX; + return do_proc_douintvec(table, write, buffer, lenp, ppos, do_proc_douintvec_minmax_conv, ¶m); } @@ -950,23 +903,17 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table tmp; - unsigned int min = 0, max = 255U, val; + unsigned int val; u8 *data = table->data; - struct do_proc_douintvec_minmax_conv_param param = { - .min = &min, - .max = &max, - }; + struct do_proc_minmax_conv_param param; int res; /* Do not support arrays yet. */ if (table->maxlen != sizeof(u8)) return -EINVAL; - if (table->extra1) - min = *(unsigned int *) table->extra1; - if (table->extra2) - max = *(unsigned int *) table->extra2; - + param.min = (table->extra1) ? *(unsigned int *) table->extra1 : 0; + param.max = (table->extra2) ? *(unsigned int *) table->extra2 : 255U; tmp = *table; tmp.maxlen = sizeof(val); @@ -1007,7 +954,7 @@ static int __do_proc_doulongvec_minmax(void *data, void *buffer, size_t *lenp, loff_t *ppos, unsigned long convmul, unsigned long convdiv) { - unsigned long *i, *min, *max; + unsigned long *i, min, max; int vleft, first = 1, err = 0; size_t left; char *p; @@ -1018,8 +965,9 @@ static int __do_proc_doulongvec_minmax(void *data, } i = data; - min = table->extra1; - max = table->extra2; + min = (table->extra1) ? *(unsigned long *) table->extra1 : 0; + max = (table->extra2) ? *(unsigned long *) table->extra2 : ULONG_MAX; + vleft = table->maxlen / sizeof(unsigned long); left = *lenp; @@ -1051,7 +999,7 @@ static int __do_proc_doulongvec_minmax(void *data, } val = convmul * val / convdiv; - if ((min && val < *min) || (max && val > *max)) { + if ((val < min) || (val > max)) { err = -EINVAL; break; } @@ -1209,7 +1157,7 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv int *valp, int write, void *data) { int tmp, ret; - struct do_proc_dointvec_minmax_conv_param *param = data; + struct do_proc_minmax_conv_param *param = data; /* * If writing, first do so via a temporary local int so we can * bounds-check it before touching *valp. @@ -1221,8 +1169,7 @@ static int do_proc_dointvec_ms_jiffies_minmax_conv(bool *negp, unsigned long *lv return ret; if (write) { - if ((param->min && *param->min > tmp) || - (param->max && *param->max < tmp)) + if ((param->min > tmp) || (param->max < tmp)) return -EINVAL; *valp = tmp; } @@ -1254,10 +1201,10 @@ int proc_dointvec_jiffies(const struct ctl_table *table, int write, int proc_dointvec_ms_jiffies_minmax(const 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, - }; + struct do_proc_minmax_conv_param param; + + param.min = (table->extra1) ? *(int *) table->extra1 : INT_MIN; + param.max = (table->extra2) ? *(int *) table->extra2 : INT_MAX; return do_proc_dointvec(table, write, buffer, lenp, ppos, do_proc_dointvec_ms_jiffies_minmax_conv, ¶m); } From patchwork Sun Feb 9 15:58:10 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wen Yang X-Patchwork-Id: 13966993 Received: from out-186.mta1.migadu.com (out-186.mta1.migadu.com [95.215.58.186]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6ABD01DF973 for ; Sun, 9 Feb 2025 15:59:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.186 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116786; cv=none; b=dWw49sd+9M7APImD3+i4k8pkmKb1HptCqLUJ3oXh+wu6oAUESj0ctF4hnR6VRze1OQhUIrRm0tUYneFp3yt4Eo8xDc9cI270j1R57X3H+cltZCQwreE3Xpsgg/BM59p/4GtdfoAazOBLdo6MhIXnL2gbNvDzo8p/S9wQW3XP06A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116786; c=relaxed/simple; bh=6ISABX7eGdy00MmYucttXr7k1CYTaTF9zUkpcSwA+K0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=I4zGb39AXOAAs6rymGTvCjWpsgfIxwTmC2HnrJdl2Mhqe+5Qzv7/qkgeyOybBdqyA/6HHp2COfx/ivDNjgUyOiemO0KAft78RAhmV45oCMbRuQ8CNC2ICXs88XIwFFPjK5z7PC3bwXknOA/v11dq+gJrIbBaCvIa5FbYn8gS5hI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=L4j9nbBm; arc=none smtp.client-ip=95.215.58.186 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="L4j9nbBm" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1739116782; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gCCPVs2TAQnjOqMj3iVhhjeMXlDSmY++BFhW3cMxNE4=; b=L4j9nbBmukGNl/2xMLcQeWLBkBxLJZg6N5i8rC5PunMNx03AGygY0XZuhjNOelp1jwgfRL Q2bs8YIkDftYdTRCKTV5mNE+xC3XgzVPUTf2UIAh/dhz9YZihk5EHe0j5LNAPBfkAZTcZs HIj+of8KUwl5SSyyr0dMvNSR74MmXOc= From: Wen Yang To: Joel Granados , Luis Chamberlain , Kees Cook Cc: "Eric W . Biederman" , Dave Young , Christian Brauner , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Wen Yang Subject: [PATCH v5 2/5] sysctl: add helper functions to extract table->extra1/extra2 Date: Sun, 9 Feb 2025 23:58:10 +0800 Message-Id: In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT Add some sysctl helper functions to avoid direct access to table->extra1/extra2. Signed-off-by: Wen Yang Cc: Luis Chamberlain Cc: Kees Cook Cc: Joel Granados Cc: Eric W. Biederman Cc: Christian Brauner Cc: Dave Young Cc: linux-kernel@vger.kernel.org --- fs/proc/proc_sysctl.c | 21 +++++++++----------- include/linux/sysctl.h | 44 ++++++++++++++++++++++++++++++++++++++++++ kernel/sysctl.c | 21 ++++++++++---------- 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 27a283d85a6e..6649d1db5f8f 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -1115,18 +1115,15 @@ static int sysctl_check_table_array(const char *path, const struct ctl_table *ta if (table->maxlen != sizeof(u8)) err |= sysctl_err(path, table, "array not allowed"); - if (table->extra1) { - extra = *(unsigned int *) table->extra1; - if (extra > 255U) - err |= sysctl_err(path, table, - "range value too large for proc_dou8vec_minmax"); - } - if (table->extra2) { - extra = *(unsigned int *) table->extra2; - if (extra > 255U) - err |= sysctl_err(path, table, - "range value too large for proc_dou8vec_minmax"); - } + extra = sysctl_range_min_u8(table); + if (extra > 255U) + err |= sysctl_err(path, table, + "range value too large for proc_dou8vec_minmax\n"); + + extra = sysctl_range_max_u8(table); + if (extra > 255U) + err |= sysctl_err(path, table, + "range value too large for proc_dou8vec_minmax\n"); } if (table->proc_handler == proc_dobool) { diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 40a6ac6c9713..eee8480dc069 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -210,6 +210,50 @@ struct ctl_table_root { #define register_sysctl(path, table) \ register_sysctl_sz(path, table, ARRAY_SIZE(table)) +#define __SYSCTL_RANGE_MIN(_a, _b, _c) (((_a)->extra1) ? *(_b((_a)->extra1)) : (_c)) + +#define __SYSCTL_RANGE_MAX(_a, _b, _c) (((_a)->extra2) ? *(_b((_a)->extra2)) : (_c)) + +static inline unsigned int sysctl_range_min_u8(const struct ctl_table *table) +{ + return (unsigned int)__SYSCTL_RANGE_MIN(table, (unsigned int *), 0); +} + +static inline unsigned int sysctl_range_max_u8(const struct ctl_table *table) +{ + return (unsigned int)__SYSCTL_RANGE_MAX(table, (unsigned int *), U8_MAX); +} + +static inline int sysctl_range_min_int(const struct ctl_table *table) +{ + return (int)__SYSCTL_RANGE_MIN(table, (int *), INT_MIN); +} + +static inline int sysctl_range_max_int(const struct ctl_table *table) +{ + return (int)__SYSCTL_RANGE_MAX(table, (int *), INT_MAX); +} + +static inline unsigned int sysctl_range_min_uint(const struct ctl_table *table) +{ + return (unsigned int)__SYSCTL_RANGE_MIN(table, (unsigned int *), 0); +} + +static inline unsigned int sysctl_range_max_uint(const struct ctl_table *table) +{ + return (unsigned int)__SYSCTL_RANGE_MAX(table, (unsigned int *), UINT_MAX); +} + +static inline unsigned long sysctl_range_min_ulong(const struct ctl_table *table) +{ + return (unsigned long)__SYSCTL_RANGE_MIN(table, (unsigned long *), 0); +} + +static inline unsigned long sysctl_range_max_ulong(const struct ctl_table *table) +{ + return (unsigned long)__SYSCTL_RANGE_MAX(table, (unsigned long *), ULONG_MAX); +} + #ifdef CONFIG_SYSCTL void proc_sys_poll_notify(struct ctl_table_poll *poll); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 4507795e3568..b745445cbfbf 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -845,8 +845,8 @@ int proc_dointvec_minmax(const struct ctl_table *table, int write, { struct do_proc_minmax_conv_param param; - param.min = (table->extra1) ? *(int *) table->extra1 : INT_MIN; - param.max = (table->extra2) ? *(int *) table->extra2 : INT_MAX; + param.min = sysctl_range_min_int(table); + param.max = sysctl_range_max_int(table); return do_proc_dointvec(table, write, buffer, lenp, ppos, do_proc_dointvec_minmax_conv, ¶m); } @@ -875,9 +875,8 @@ int proc_douintvec_minmax(const struct ctl_table *table, int write, { struct do_proc_minmax_conv_param param; - param.min = (table->extra1) ? *(unsigned int *) table->extra1 : 0; - param.max = (table->extra2) ? *(unsigned int *) table->extra2 : UINT_MAX; - + param.min = sysctl_range_min_uint(table); + param.max = sysctl_range_max_uint(table); return do_proc_douintvec(table, write, buffer, lenp, ppos, do_proc_douintvec_minmax_conv, ¶m); } @@ -912,8 +911,8 @@ int proc_dou8vec_minmax(const struct ctl_table *table, int write, if (table->maxlen != sizeof(u8)) return -EINVAL; - param.min = (table->extra1) ? *(unsigned int *) table->extra1 : 0; - param.max = (table->extra2) ? *(unsigned int *) table->extra2 : 255U; + param.min = sysctl_range_min_u8(table); + param.max = sysctl_range_max_u8(table); tmp = *table; tmp.maxlen = sizeof(val); @@ -965,8 +964,8 @@ static int __do_proc_doulongvec_minmax(void *data, } i = data; - min = (table->extra1) ? *(unsigned long *) table->extra1 : 0; - max = (table->extra2) ? *(unsigned long *) table->extra2 : ULONG_MAX; + min = sysctl_range_min_ulong(table); + max = sysctl_range_max_ulong(table); vleft = table->maxlen / sizeof(unsigned long); left = *lenp; @@ -1203,8 +1202,8 @@ int proc_dointvec_ms_jiffies_minmax(const struct ctl_table *table, int write, { struct do_proc_minmax_conv_param param; - param.min = (table->extra1) ? *(int *) table->extra1 : INT_MIN; - param.max = (table->extra2) ? *(int *) table->extra2 : INT_MAX; + param.min = sysctl_range_min_int(table); + param.max = sysctl_range_max_int(table); return do_proc_dointvec(table, write, buffer, lenp, ppos, do_proc_dointvec_ms_jiffies_minmax_conv, ¶m); } From patchwork Sun Feb 9 15:58:11 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wen Yang X-Patchwork-Id: 13966994 Received: from out-185.mta1.migadu.com (out-185.mta1.migadu.com [95.215.58.185]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 498EE1DF960 for ; Sun, 9 Feb 2025 15:59:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.185 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116798; cv=none; b=OL7ZJ6aplofJkFQdGB86xzj7OwyC9H7P6yDSLAm+Ab2mG77+lLU5cNJ4P6koj9zxKVWKqQJHrpQBEw7h3VD45zJgOqA/quks6DZM7/QxuhaLTGj6kGY2wuK3PIDojXhefdwKbQzpiuNLj3cfnOXp+xeNYBBIf5KqtvU0ZF1bx90= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116798; c=relaxed/simple; bh=/3jPuGA7RdR76pPj01WKfZd8NrowcftvpO2zikJz8HM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=W2YmVH9Vv9Bs8HQiv8Q62mUOrjbA85PnknNVb4IYWfHtTVQ3cbIc3YlOe4MTctSahOhV3bUL6nIH25iCWEJWMX9Iw6z+px79Q1liyAFPfPFLhEfRDIe6O8lTqGZyH4pG/tcrxwE3vnlR7CqugLvRWZS/UU0UOW7q4XP0jtWSnAw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=bi9vmoYR; arc=none smtp.client-ip=95.215.58.185 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="bi9vmoYR" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1739116794; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=z4wha4ZKGWvo2tXOl25CgTv+1PAxRxNqQziVf5W8hTA=; b=bi9vmoYRqXyf9gJdY9GGFT5y9OS6IN2UBMxPzw5Nf4PXNWyIdRVdEew+5d7OrBLOs1MOhD lYwKDj+7zX3D0CJ6Yg3ijk3+2R6nk/hscMvhd2UINHLmKxrZ8hkTRGWGMuHgBxF4gnaAU2 cVupejRdhMqyUW8P4l36gu1Kx1VNL3I= From: Wen Yang To: Joel Granados , Luis Chamberlain , Kees Cook Cc: "Eric W . Biederman" , Dave Young , Christian Brauner , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Wen Yang Subject: [PATCH v5 3/5] sysctl: support encoding values directly in the table entry Date: Sun, 9 Feb 2025 23:58:11 +0800 Message-Id: <8acb6aee7aedb1c816012ebe48d1605e4ec5a3ab.1739115369.git.wen.yang@linux.dev> In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT Eric points out: "by turning .extra1 and .extra2 into longs instead of keeping them as pointers and needing constants to be pointed at somewhere .. The only people I can see who find a significant benefit by consolidating all of the constants into one place are people who know how to stomp kernel memory." This patch supports encoding values directly in table entries through the following work: - extra1/extra2 and min/max are placed in one union to ensure compatibility with previous code without increasing memory space, and then we could gradually remove these unnecessary extra1/extra2. - two bits were used to represent the information of the above union: SYSCTL_FLAG_MIN: 0 - using extra1, 1 - using min. SYSCTL_FLAG_MAX: 0 - using extra2, 2 - using max. - since the proc file's mode field only uses 9 bits(777), we could use the additional two bits(S_ISUID and S_ISGID) to temporarily represent SYSCTL_FLAG_MIN and SYSCTL_FLAG_MAX. - added some helper macros. Signed-off-by: Wen Yang Cc: Luis Chamberlain Cc: Kees Cook Cc: Joel Granados Cc: Eric W. Biederman Cc: Christian Brauner Cc: Dave Young Cc: linux-kernel@vger.kernel.org --- fs/proc/proc_sysctl.c | 14 ++++++++++++-- include/linux/sysctl.h | 24 ++++++++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 6649d1db5f8f..08b9cfb11165 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -848,9 +848,18 @@ static int proc_sys_getattr(struct mnt_idmap *idmap, return PTR_ERR(head); generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); - if (table) + if (table) { stat->mode = (stat->mode & S_IFMT) | table->mode; + /* + * S_ISUID and S_ISGID are to temporarily represent + * the internal SYSCTL_FLAG_MIN and SYSCTL_FLAG_MAX, + * and are not visible to users + */ + stat->mode &= ~SYSCTL_FLAG_MIN; + stat->mode &= ~SYSCTL_FLAG_MAX; + } + sysctl_head_finish(head); return 0; } @@ -1163,7 +1172,8 @@ static int sysctl_check_table(const char *path, struct ctl_table_header *header) if (!entry->proc_handler) err |= sysctl_err(path, entry, "No proc_handler"); - if ((entry->mode & (S_IRUGO|S_IWUGO)) != entry->mode) + if ((entry->mode & (S_IRUGO|S_IWUGO|SYSCTL_FLAG_MIN|SYSCTL_FLAG_MAX)) + != entry->mode) err |= sysctl_err(path, entry, "bogus .mode 0%o", entry->mode); } diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index eee8480dc069..f07a3f0fb3b1 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -28,6 +28,7 @@ #include #include #include +#include /* For the /proc/sys support */ struct completion; @@ -129,6 +130,9 @@ static inline void *proc_sys_poll_event(struct ctl_table_poll *poll) #define DEFINE_CTL_TABLE_POLL(name) \ struct ctl_table_poll name = __CTL_TABLE_POLL_INITIALIZER(name) +#define SYSCTL_FLAG_MIN S_ISUID +#define SYSCTL_FLAG_MAX S_ISGID + /* A sysctl table is an array of struct ctl_table: */ struct ctl_table { const char *procname; /* Text ID for /proc/sys */ @@ -137,8 +141,16 @@ struct ctl_table { umode_t mode; proc_handler *proc_handler; /* Callback for text formatting */ struct ctl_table_poll *poll; - void *extra1; - void *extra2; + union { + struct { + void *extra1; + void *extra2; + }; + struct { + unsigned long min; + unsigned long max; + }; + }; } __randomize_layout; struct ctl_node { @@ -210,9 +222,13 @@ struct ctl_table_root { #define register_sysctl(path, table) \ register_sysctl_sz(path, table, ARRAY_SIZE(table)) -#define __SYSCTL_RANGE_MIN(_a, _b, _c) (((_a)->extra1) ? *(_b((_a)->extra1)) : (_c)) +#define __SYSCTL_RANGE_EXTRA1(_a, _b, _c) (((_a)->extra1) ? *(_b((_a)->extra1)) : (_c)) +#define __SYSCTL_RANGE_MIN(_a, _b, _c) ((((_a)->mode) & SYSCTL_FLAG_MIN) ? \ + ((_a)->min) : __SYSCTL_RANGE_EXTRA1(_a, _b, _c)) -#define __SYSCTL_RANGE_MAX(_a, _b, _c) (((_a)->extra2) ? *(_b((_a)->extra2)) : (_c)) +#define __SYSCTL_RANGE_EXTRA2(_a, _b, _c) (((_a)->extra2) ? *(_b((_a)->extra2)) : (_c)) +#define __SYSCTL_RANGE_MAX(_a, _b, _c) ((((_a)->mode) & SYSCTL_FLAG_MAX) ? \ + ((_a)->max) : __SYSCTL_RANGE_EXTRA2(_a, _b, _c)) static inline unsigned int sysctl_range_min_u8(const struct ctl_table *table) { From patchwork Sun Feb 9 15:58:12 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wen Yang X-Patchwork-Id: 13966995 Received: from out-189.mta1.migadu.com (out-189.mta1.migadu.com [95.215.58.189]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 467A51DF967 for ; Sun, 9 Feb 2025 16:00:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.189 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116809; cv=none; b=SSbzncp5XiWHXgYfEBle4UfCx9WUn068ZASRCMeTHb+IgzhXT3ts3LVehZhC5arM2xPDsJp3s7fFs5SWoaQjMqS2xSqh3lQqWLHHXM4vS9RB3RL+EhcQZyP+cFarMkZMk4ue/j3nZkXi7hRru7hHLMczKYz0FLwGy/CRF3+K+LA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116809; c=relaxed/simple; bh=NNgJ24uoHbg38byL385xCjee8sVFTOFfnx8XEp96pkU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=JwaTXC8pls788QiAZ72FtFspU3G3zu369+3eclmqfeJ+ztuq6s50qBcWu97rdc76DtwLzmjqv628un3f/INc/5otnvi3UxUc7r3tZLCiAyjAYyIurs2UFYoyDq1FLSrKWtR+Soljio9MdvWuFcZqTmQslHN9GhMOlICxBzZif7E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=v28pio5z; arc=none smtp.client-ip=95.215.58.189 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="v28pio5z" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1739116804; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=K7OinkOZF/f7EoTMzksKY7Lf0HZK7M3o3Djrf3Zs1d8=; b=v28pio5zi/cKH6WpK6XSQaPvdu9lh8zpZvmTwrsNOGQH7V918wuakfKDnvJ+p97YyzAvyQ v/z1Gb2p+nMUWGLJSCauquseZEPZnkyWKd+91TFZ2xzANZfk3RPQ6Bpqdsy7KdaCV55Dgf nLLPCJK5CYMGTxjGocs/D/LNUPw9rig= From: Wen Yang To: Joel Granados , Luis Chamberlain , Kees Cook Cc: "Eric W . Biederman" , Dave Young , Christian Brauner , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Wen Yang Subject: [PATCH v5 4/5] sysctl: add kunit test code to check the min/max encoding of sysctl table entries Date: Sun, 9 Feb 2025 23:58:12 +0800 Message-Id: <9b0ef52afdb154d43cb93a8c5ab71125270f50bf.1739115369.git.wen.yang@linux.dev> In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT Add kunit test code to check the impact of encoding the min/max values directly in table entries on functions such as proc_rointvec, proc_rou8vectminmax, proc_rouintvectminmax, and proc_roulongvectminmax, including basic parsing testing and min/max overflow testing. Signed-off-by: Wen Yang Cc: Luis Chamberlain Cc: Kees Cook Cc: Joel Granados Cc: Eric W. Biederman Cc: Christian Brauner Cc: Dave Young Cc: linux-kernel@vger.kernel.org --- kernel/sysctl-test.c | 581 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 581 insertions(+) diff --git a/kernel/sysctl-test.c b/kernel/sysctl-test.c index 3ac98bb7fb82..47064ab8b7f3 100644 --- a/kernel/sysctl-test.c +++ b/kernel/sysctl-test.c @@ -415,6 +415,575 @@ static void sysctl_test_register_sysctl_sz_invalid_extra_value( KUNIT_EXPECT_NOT_NULL(test, register_sysctl("foo", table_qux)); } +static void sysctl_test_api_dointvec_write_with_minmax( + struct kunit *test) +{ + int data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(int), + .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX, + .proc_handler = proc_dointvec_minmax, + .min = -1, + .max = 100, + }; + size_t max_len = 32, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "10"; + char input2[] = "-5"; + char input3[] = "200"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 10, *((int *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 10, *((int *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 10, *((int *)table.data)); +} + +static void sysctl_test_api_dointvec_write_with_min( + struct kunit *test) +{ + int data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(int), + .mode = 0644 | SYSCTL_FLAG_MIN, + .proc_handler = proc_dointvec_minmax, + .min = -1, + }; + size_t max_len = 32, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "10"; + char input2[] = "-5"; + char input3[] = "2147483647"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 10, *((int *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 10, *((int *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len); + KUNIT_EXPECT_EQ(test, 2147483647, *((int *)table.data)); +} + +static void sysctl_test_api_dointvec_write_with_max( + struct kunit *test) +{ + int data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(int), + .mode = 0644 | SYSCTL_FLAG_MAX, + .proc_handler = proc_dointvec_minmax, + .max = 100, + }; + size_t max_len = 32, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "10"; + char input2[] = "2147483647"; + char input3[] = "-2147483648"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 10, *((int *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 10, *((int *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, 0, proc_dointvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len); + KUNIT_EXPECT_EQ(test, -2147483648, *((int *)table.data)); +} + +static void sysctl_test_api_douintvec_write_with_minmax( + struct kunit *test) +{ + unsigned int data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(int), + .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX, + .proc_handler = proc_douintvec_minmax, + .min = 4, + .max = 200, + }; + size_t max_len = 32, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "100"; + char input2[] = "3"; + char input3[] = "1000"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data)); +} + +static void sysctl_test_api_douintvec_write_with_min( + struct kunit *test) +{ + unsigned int data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(int), + .mode = 0644 | SYSCTL_FLAG_MIN, + .proc_handler = proc_douintvec_minmax, + .min = 4, + }; + size_t max_len = 32, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "100"; + char input2[] = "3"; + char input3[] = "4294967295"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 100, *((unsigned int *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len); + KUNIT_EXPECT_EQ(test, 4294967295, *((unsigned int *)table.data)); +} + +static void sysctl_test_api_douintvec_write_with_max( + struct kunit *test) +{ + unsigned int data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(int), + .mode = 0644 | SYSCTL_FLAG_MAX, + .proc_handler = proc_douintvec_minmax, + .max = 1000, + }; + size_t max_len = 32, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "900"; + char input2[] = "10000"; + char input3[] = "0"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 900, *((unsigned int *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 900, *((unsigned int *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, 0, proc_douintvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len); + KUNIT_EXPECT_EQ(test, 0, *((unsigned int *)table.data)); +} + +static void sysctl_test_api_dou8vec_write_with_minmax( + struct kunit *test) +{ + unsigned char data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(unsigned char), + .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX, + .proc_handler = proc_dou8vec_minmax, + .min = 3, + .max = 100, + }; + size_t max_len = 8, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "32"; + char input2[] = "1"; + char input3[] = "200"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data)); +} + +static void sysctl_test_api_dou8vec_write_with_min( + struct kunit *test) +{ + unsigned char data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(unsigned char), + .mode = 0644 | SYSCTL_FLAG_MIN, + .proc_handler = proc_dou8vec_minmax, + .min = 3, + }; + size_t max_len = 8, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "32"; + char input2[] = "1"; + char input3[] = "255"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len); + KUNIT_EXPECT_EQ(test, 255, *((unsigned char *)table.data)); +} + +static void sysctl_test_api_dou8vec_write_with_max( + struct kunit *test) +{ + unsigned char data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(unsigned char), + .mode = 0644 | SYSCTL_FLAG_MAX, + .proc_handler = proc_dou8vec_minmax, + .max = 200, + }; + size_t max_len = 8, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "32"; + char input2[] = "0"; + char input3[] = "255"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 32, *((unsigned char *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, 0, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input2) - 1, len); + KUNIT_EXPECT_EQ(test, 0, *((unsigned char *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_dou8vec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 0, *((unsigned char *)table.data)); +} + +static void sysctl_test_api_doulongvec_write_with_minmax( + struct kunit *test) +{ + unsigned long data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(unsigned long), + .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX, + .proc_handler = proc_doulongvec_minmax, + .min = 1000, + .max = 3000, + }; + size_t max_len = 64, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "1024"; + char input2[] = "100"; + char input3[] = "4096"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data)); +} + +static void sysctl_test_api_doulongvec_write_with_min( + struct kunit *test) +{ + unsigned long data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(unsigned long), + .mode = 0644 | SYSCTL_FLAG_MIN, + .proc_handler = proc_doulongvec_minmax, + .min = 1000, + }; + size_t max_len = 64, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "1000"; + char input2[] = "10"; + char input3[64] = {0}; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 1000, *((unsigned long *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 1000, *((unsigned long *)table.data)); + + // test for input3 + snprintf(input3, sizeof(input3), "%lu", ULONG_MAX); + len = strlen(input3); + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, strlen(input3), len); + KUNIT_EXPECT_EQ(test, ULONG_MAX, *((unsigned long *)table.data)); +} + +static void sysctl_test_api_doulongvec_write_with_max( + struct kunit *test) +{ + unsigned long data = 0; + const struct ctl_table table = { + .procname = "foo", + .data = &data, + .maxlen = sizeof(unsigned long), + .mode = 0644 | SYSCTL_FLAG_MAX, + .proc_handler = proc_doulongvec_minmax, + .max = 3000, + }; + size_t max_len = 64, len = max_len; + loff_t pos = 0; + char *buffer = kunit_kzalloc(test, max_len, GFP_USER); + char __user *user_buffer = (char __user *)buffer; + char input1[] = "1024"; + char input2[] = "4096"; + char input3[] = "0"; + + // test for input1 + len = sizeof(input1) - 1; + memcpy(buffer, input1, len); + KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input1) - 1, len); + KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data)); + + // test for input2 + len = sizeof(input2) - 1; + pos = 0; + memcpy(buffer, input2, len); + KUNIT_EXPECT_EQ(test, -EINVAL, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, 0, pos); + KUNIT_EXPECT_EQ(test, 1024, *((unsigned long *)table.data)); + + // test for input3 + len = sizeof(input3) - 1; + pos = 0; + memcpy(buffer, input3, len); + KUNIT_EXPECT_EQ(test, 0, proc_doulongvec_minmax(&table, KUNIT_PROC_WRITE, + user_buffer, &len, &pos)); + KUNIT_EXPECT_EQ(test, sizeof(input3) - 1, len); + KUNIT_EXPECT_EQ(test, 0, *((unsigned long *)table.data)); +} + static struct kunit_case sysctl_test_cases[] = { KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data), KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset), @@ -427,6 +996,18 @@ static struct kunit_case sysctl_test_cases[] = { KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min), KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max), KUNIT_CASE(sysctl_test_register_sysctl_sz_invalid_extra_value), + KUNIT_CASE(sysctl_test_api_dointvec_write_with_minmax), + KUNIT_CASE(sysctl_test_api_dointvec_write_with_min), + KUNIT_CASE(sysctl_test_api_dointvec_write_with_max), + KUNIT_CASE(sysctl_test_api_douintvec_write_with_minmax), + KUNIT_CASE(sysctl_test_api_douintvec_write_with_min), + KUNIT_CASE(sysctl_test_api_douintvec_write_with_max), + KUNIT_CASE(sysctl_test_api_dou8vec_write_with_minmax), + KUNIT_CASE(sysctl_test_api_dou8vec_write_with_min), + KUNIT_CASE(sysctl_test_api_dou8vec_write_with_max), + KUNIT_CASE(sysctl_test_api_doulongvec_write_with_minmax), + KUNIT_CASE(sysctl_test_api_doulongvec_write_with_min), + KUNIT_CASE(sysctl_test_api_doulongvec_write_with_max), {} }; From patchwork Sun Feb 9 15:58:13 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wen Yang X-Patchwork-Id: 13966996 Received: from out-171.mta1.migadu.com (out-171.mta1.migadu.com [95.215.58.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 415EB1DF733 for ; Sun, 9 Feb 2025 16:00:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116823; cv=none; b=j4i2Hg+ntZVKN6UPcAco+Bd5YVKAHmk2sWWjp54DUPfIQLB/A/t8dkAv1A2NqGXzM/a73OUYmbK3NWyCWafG2JoGfhNPWqyswpFxD77voeuiR/a0dLVtiyssoasVK8J6A2qTDK8aMWPlXSDmkv+oVAJAkTePSsU3gGOCEvU08Ts= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1739116823; c=relaxed/simple; bh=UDnxwVq6Dye081rKerjJW9pqZ7LWi0qgUZNCzQNN81M=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=YuOh4FQzyenBiQ0gwpETKOhyhOQFOvP8OTcQGqn0gWpIqfY5e32P6SmSAChm5Y2DhBVofZgJX53tPkf+39aFYwSspqGV2bxECKdaittUvbK7YUHKilfouf36RhE/taKk1ITo/cl9YFltUk0egQIxSbY/Pj/KKTM7//l5D8KQY64= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=WtMeLbHK; arc=none smtp.client-ip=95.215.58.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="WtMeLbHK" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1739116819; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=185pKD1tTovZhp7hwS46AH4uWT2/AwtNcxiuhT9N49U=; b=WtMeLbHK0inVSpYW7o7qhl7xKsw+RFo2zvOMRyMBLg8/n3pCO1mFxHteaGfYKlRviDJ0Bm O7/xV4s+Nmt7Wc0rxNulFKg5CRkgEp9c2tnM3gq+e6dXV9gfvrH/NUG0h8o6+9ZUdanxEA acpA7qpQWyMY+itd5JnpimJ+SkZCnys= From: Wen Yang To: Joel Granados , Luis Chamberlain , Kees Cook Cc: "Eric W . Biederman" , Dave Young , Christian Brauner , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Wen Yang Subject: [PATCH v5 5/5] sysctl: delete six_hundred_forty_kb to save 4 bytes Date: Sun, 9 Feb 2025 23:58:13 +0800 Message-Id: <0432e1fecb00721113c93b3d4d908b452c90f71b.1739115369.git.wen.yang@linux.dev> In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT By directly encoding specific numbers into the min/max field, unnecessary global variable six_hundred_forty_kb can be removed, saving 4 bytes Signed-off-by: Wen Yang Cc: Luis Chamberlain Cc: Kees Cook Cc: Joel Granados Cc: Eric W. Biederman Cc: Christian Brauner Cc: Dave Young Cc: linux-kernel@vger.kernel.org --- kernel/sysctl.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index b745445cbfbf..a9ef3b4d736c 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -81,13 +81,6 @@ EXPORT_SYMBOL_GPL(sysctl_long_vals); #if defined(CONFIG_SYSCTL) -/* Constants used for minimum and maximum */ - -#ifdef CONFIG_PERF_EVENTS -static const int six_hundred_forty_kb = 640 * 1024; -#endif - - static const int ngroups_max = NGROUPS_MAX; static const int cap_last_cap = CAP_LAST_CAP; @@ -1915,10 +1908,10 @@ static struct ctl_table kern_table[] = { .procname = "perf_event_max_stack", .data = &sysctl_perf_event_max_stack, .maxlen = sizeof(sysctl_perf_event_max_stack), - .mode = 0644, + .mode = 0644 | SYSCTL_FLAG_MIN | SYSCTL_FLAG_MAX, .proc_handler = perf_event_max_stack_handler, - .extra1 = SYSCTL_ZERO, - .extra2 = (void *)&six_hundred_forty_kb, + .min = 0, + .max = 640 * 1024, }, { .procname = "perf_event_max_contexts_per_stack",