From: Rodrigo Alencar <rodrigo.alencar@analog.com>
Add iio_str_to_fixpoint64() function that leverages simple_strtoull()
to parse numbers from a string.
A helper function __iio_str_to_fixpoint64() replaces
__iio_str_to_fixpoint() implementation, extending its usage for
64-bit fixed-point parsing.
Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
---
drivers/iio/industrialio-core.c | 211 ++++++++++++++++++++++++++++++----------
include/linux/iio/iio.h | 2 +
2 files changed, 163 insertions(+), 50 deletions(-)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 3115d59c1372..37e9ed6b659b 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/overflow.h>
#include <linux/poll.h>
#include <linux/property.h>
#include <linux/sched.h>
@@ -881,6 +882,136 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
}
}
+/**
+ * iio_safe_strntou64() - Parse u64 from string checking for overflow safety
+ * @str: The string to parse
+ * @endp: output pointer to the end parsing position
+ * @result: parsed value
+ * @max_chars: maximum number of digit characters to read
+ *
+ * This function is used in fixed-point parsing and it iterates over a const
+ * char array. It might duplicate behavior of simple_strtoull() or kstrtoull(),
+ * but those have their own limitations:
+ * - simple_strtoull() is not overflow-safe and its usage is discouraged;
+ * - kstrtoull() is safe, but requires termination and it would required a copy
+ * of the string to a temporary buffer.
+ *
+ * The implementation of this function is similar to _parse_integer_limit()
+ * available in lib/kstrtox.h, but that header/function is not available to be
+ * used in kernel modules. Hence, this implementation may need to change or
+ * removed to reuse a new suitable helper that is properly exposed.
+ *
+ * Returns:
+ * number of parsed characters on success, -ERANGE on overflow
+ */
+static ssize_t iio_safe_strntou64(const char *str, const char **endp,
+ u64 *result, size_t max_chars)
+{
+ u64 digit, acc = 0;
+ ssize_t idx = 0;
+
+ while (isdigit(str[idx]) && idx < max_chars) {
+ digit = str[idx] - '0';
+ if (unlikely(acc & (~0ull << 60))) {
+ if (check_mul_overflow(acc, 10, &acc) ||
+ check_add_overflow(acc, digit, &acc))
+ return -ERANGE;
+ } else {
+ acc = acc * 10 + digit;
+ }
+ idx++;
+ }
+
+ *endp = str + idx;
+ *result = acc;
+ return idx;
+}
+
+/**
+ * __iio_str_to_fixpoint64() - Parse a fixed-point number from a string
+ * @str: The string to parse
+ * @fract_mult: Multiplier for the first decimal place, should be a power of 10
+ * @integer: The integer part of the number
+ * @fract: The fractional part of the number
+ * @scale_db: True if this should parse as dB
+ *
+ * This variant uses 64-bit integers for both integer and fractional parts.
+ * Parsed positive values greater than S64_MAX are returned as-is. Parsed
+ * negative values less than S64_MIN are treated as range error, so -ERANGE is
+ * returned.
+ *
+ * Returns:
+ * 0 on success, or a negative error code if the string could not be parsed.
+ */
+static int __iio_str_to_fixpoint64(const char *str, u64 fract_mult,
+ s64 *integer, s64 *fract, bool scale_db)
+{
+ u64 i = 0, f = 0;
+ int ret, precision = ffs(fract_mult);
+ bool negative = false;
+
+ if (precision > 20) /* ceil(log10(U64_MAX)) = 20 */
+ return -EINVAL;
+
+ if (str[0] == '-') {
+ negative = true;
+ str++;
+ } else if (str[0] == '+') {
+ str++;
+ }
+
+ ret = iio_safe_strntou64(str, &str, &i, SIZE_MAX);
+ if (ret < 0)
+ return ret;
+
+ if (precision && *str == '.') {
+ str++; /* skip decimal point */
+ ret = iio_safe_strntou64(str, &str, &f, precision);
+ if (ret < 0)
+ return ret;
+
+ if (ret < precision) /* scale up */
+ f *= int_pow(10, precision - ret);
+
+ while (isdigit(*str)) /* truncate: ignore further digits */
+ str++;
+ }
+
+ if (!ret)
+ return -EINVAL;
+
+ if (scale_db) {
+ /* Ignore the dB suffix */
+ if (!strncmp(str, " dB", sizeof(" dB") - 1))
+ str += sizeof(" dB") - 1;
+ else if (!strncmp(str, "dB", sizeof("dB") - 1))
+ str += sizeof("dB") - 1;
+ }
+
+ if (*str == '\n')
+ str++;
+
+ if (*str != '\0')
+ return -EINVAL;
+
+ if (negative) {
+ if (i) {
+ if (i > (u64)S64_MIN)
+ return -ERANGE;
+ i = -i;
+ } else {
+ if (f > (u64)S64_MIN)
+ return -ERANGE;
+ f = -f;
+ }
+ }
+
+ *integer = i;
+ *fract = f;
+
+ return 0;
+}
+
/**
* __iio_str_to_fixpoint() - Parse a fixed-point number from a string
* @str: The string to parse
@@ -895,63 +1026,43 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
static int __iio_str_to_fixpoint(const char *str, int fract_mult,
int *integer, int *fract, bool scale_db)
{
- int i = 0, f = 0;
- bool integer_part = true, negative = false;
+ s64 integer64, fract64;
+ int ret;
- if (fract_mult == 0) {
- *fract = 0;
+ ret = __iio_str_to_fixpoint64(str, fract_mult, &integer64, &fract64,
+ scale_db);
+ if (ret)
+ return ret;
- return kstrtoint(str, 0, integer);
- }
+ if (integer64 < INT_MIN || integer64 > UINT_MAX ||
+ fract64 < INT_MIN || fract64 > UINT_MAX)
+ return -ERANGE;
- if (str[0] == '-') {
- negative = true;
- str++;
- } else if (str[0] == '+') {
- str++;
- }
-
- while (*str) {
- if ('0' <= *str && *str <= '9') {
- if (integer_part) {
- i = i * 10 + *str - '0';
- } else {
- f += fract_mult * (*str - '0');
- fract_mult /= 10;
- }
- } else if (*str == '\n') {
- if (*(str + 1) == '\0')
- break;
- return -EINVAL;
- } else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) {
- /* Ignore the dB suffix */
- str += sizeof(" dB") - 1;
- continue;
- } else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) {
- /* Ignore the dB suffix */
- str += sizeof("dB") - 1;
- continue;
- } else if (*str == '.' && integer_part) {
- integer_part = false;
- } else {
- return -EINVAL;
- }
- str++;
- }
-
- if (negative) {
- if (i)
- i = -i;
- else
- f = -f;
- }
-
- *integer = i;
- *fract = f;
+ *integer = integer64;
+ *fract = fract64;
return 0;
}
+/**
+ * iio_str_to_fixpoint64() - Parse a fixed-point number from a string
+ * @str: The string to parse
+ * @fract_mult: Multiplier for the first decimal place, should be a power of 10
+ * @integer: The integer part of the number
+ * @fract: The fractional part of the number
+ *
+ * This variant uses 64-bit integers for both integer and fractional parts.
+ *
+ * Returns:
+ * 0 on success, or a negative error code if the string could not be parsed.
+ */
+int iio_str_to_fixpoint64(const char *str, u64 fract_mult, s64 *integer,
+ s64 *fract)
+{
+ return __iio_str_to_fixpoint64(str, fract_mult, integer, fract, false);
+}
+EXPORT_SYMBOL_GPL(iio_str_to_fixpoint64);
+
/**
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
* @str: The string to parse
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index a9ecff191bd9..cb30d153465a 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -1055,6 +1055,8 @@ int iio_active_scan_mask_index(struct iio_dev *indio_dev);
ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);
+int iio_str_to_fixpoint64(const char *str, u64 fract_mult, s64 *integer,
+ s64 *fract);
int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer,
int *fract);
--
2.43.0
On Fri, Jan 30, 2026 at 10:06:43AM +0000, Rodrigo Alencar via B4 Relay wrote:
> Add iio_str_to_fixpoint64() function that leverages simple_strtoull()
> to parse numbers from a string.
> A helper function __iio_str_to_fixpoint64() replaces
> __iio_str_to_fixpoint() implementation, extending its usage for
> 64-bit fixed-point parsing.
...
> +/**
> + * iio_safe_strntou64() - Parse u64 from string checking for overflow safety
> + * @str: The string to parse
> + * @endp: output pointer to the end parsing position
> + * @result: parsed value
> + * @max_chars: maximum number of digit characters to read
> + *
> + * This function is used in fixed-point parsing and it iterates over a const
> + * char array. It might duplicate behavior of simple_strtoull() or kstrtoull(),
> + * but those have their own limitations:
> + * - simple_strtoull() is not overflow-safe and its usage is discouraged;
> + * - kstrtoull() is safe, but requires termination and it would required a copy
> + * of the string to a temporary buffer.
> + *
> + * The implementation of this function is similar to _parse_integer_limit()
> + * available in lib/kstrtox.h, but that header/function is not available to be
> + * used in kernel modules. Hence, this implementation may need to change or
> + * removed to reuse a new suitable helper that is properly exposed.
> + *
> + * Returns:
> + * number of parsed characters on success, -ERANGE on overflow
> + */
> +static ssize_t iio_safe_strntou64(const char *str, const char **endp,
> + u64 *result, size_t max_chars)
> +{
> + u64 digit, acc = 0;
> + ssize_t idx = 0;
> +
> + while (isdigit(str[idx]) && idx < max_chars) {
> + digit = str[idx] - '0';
> + if (unlikely(acc & (~0ull << 60))) {
> + if (check_mul_overflow(acc, 10, &acc) ||
> + check_add_overflow(acc, digit, &acc))
> + return -ERANGE;
> + } else {
> + acc = acc * 10 + digit;
> + }
> + idx++;
> + }
> +
> + *endp = str + idx;
> + *result = acc;
> + return idx;
> +}
There is a development in the parse_integer in the lib/. I reviewed that series
and hopefully it will go in. With that done, we better reuse the lib/ function.
https://lore.kernel.org/linux-hardening/20260202115451.290173-1-dmantipov@yandex.ru/
--
With Best Regards,
Andy Shevchenko
On 26/02/04 03:45AM, Andy Shevchenko wrote:
> On Fri, Jan 30, 2026 at 10:06:43AM +0000, Rodrigo Alencar via B4 Relay wrote:
>
> > Add iio_str_to_fixpoint64() function that leverages simple_strtoull()
> > to parse numbers from a string.
> > A helper function __iio_str_to_fixpoint64() replaces
> > __iio_str_to_fixpoint() implementation, extending its usage for
> > 64-bit fixed-point parsing.
...
> > +static ssize_t iio_safe_strntou64(const char *str, const char **endp,
> > + u64 *result, size_t max_chars)
> > +{
> > + u64 digit, acc = 0;
> > + ssize_t idx = 0;
> > +
> > + while (isdigit(str[idx]) && idx < max_chars) {
> > + digit = str[idx] - '0';
> > + if (unlikely(acc & (~0ull << 60))) {
> > + if (check_mul_overflow(acc, 10, &acc) ||
> > + check_add_overflow(acc, digit, &acc))
> > + return -ERANGE;
> > + } else {
> > + acc = acc * 10 + digit;
> > + }
> > + idx++;
> > + }
> > +
> > + *endp = str + idx;
> > + *result = acc;
> > + return idx;
> > +}
>
> There is a development in the parse_integer in the lib/. I reviewed that series
> and hopefully it will go in. With that done, we better reuse the lib/ function.
>
> https://lore.kernel.org/linux-hardening/20260202115451.290173-1-dmantipov@yandex.ru/
In this patch, I see that it updates the overflow check, but I am not
seeing that function being exposed to other kernel modules.
--
Kind regards,
Rodrigo Alencar
On Wed, Feb 04, 2026 at 09:42:55AM +0000, Rodrigo Alencar wrote:
> On 26/02/04 03:45AM, Andy Shevchenko wrote:
> > On Fri, Jan 30, 2026 at 10:06:43AM +0000, Rodrigo Alencar via B4 Relay wrote:
...
> > > +static ssize_t iio_safe_strntou64(const char *str, const char **endp,
> > > + u64 *result, size_t max_chars)
> > > +{
> > > + u64 digit, acc = 0;
> > > + ssize_t idx = 0;
> > > +
> > > + while (isdigit(str[idx]) && idx < max_chars) {
> > > + digit = str[idx] - '0';
> > > + if (unlikely(acc & (~0ull << 60))) {
> > > + if (check_mul_overflow(acc, 10, &acc) ||
> > > + check_add_overflow(acc, digit, &acc))
> > > + return -ERANGE;
> > > + } else {
> > > + acc = acc * 10 + digit;
> > > + }
> > > + idx++;
> > > + }
> > > +
> > > + *endp = str + idx;
> > > + *result = acc;
> > > + return idx;
> > > +}
> >
> > There is a development in the parse_integer in the lib/. I reviewed that series
> > and hopefully it will go in. With that done, we better reuse the lib/ function.
> >
> > https://lore.kernel.org/linux-hardening/20260202115451.290173-1-dmantipov@yandex.ru/
>
> In this patch, I see that it updates the overflow check, but I am not
> seeing that function being exposed to other kernel modules.
Can the IIO be compiled as a module? If so, then we would need to export that
function. (Note, we may export only for the exact module(s) in question, so
nobody else will be able to use it. See EXPORT_SYMBOL_FOR_MODULES() macro.)
--
With Best Regards,
Andy Shevchenko
On 26/02/04 11:57AM, Andy Shevchenko wrote:
> On Wed, Feb 04, 2026 at 09:42:55AM +0000, Rodrigo Alencar wrote:
> > On 26/02/04 03:45AM, Andy Shevchenko wrote:
> > > On Fri, Jan 30, 2026 at 10:06:43AM +0000, Rodrigo Alencar via B4 Relay wrote:
>
> ...
>
> > > > +static ssize_t iio_safe_strntou64(const char *str, const char **endp,
> > > > + u64 *result, size_t max_chars)
> > > > +{
> > > > + u64 digit, acc = 0;
> > > > + ssize_t idx = 0;
> > > > +
> > > > + while (isdigit(str[idx]) && idx < max_chars) {
> > > > + digit = str[idx] - '0';
> > > > + if (unlikely(acc & (~0ull << 60))) {
> > > > + if (check_mul_overflow(acc, 10, &acc) ||
> > > > + check_add_overflow(acc, digit, &acc))
> > > > + return -ERANGE;
> > > > + } else {
> > > > + acc = acc * 10 + digit;
> > > > + }
> > > > + idx++;
> > > > + }
> > > > +
> > > > + *endp = str + idx;
> > > > + *result = acc;
> > > > + return idx;
> > > > +}
> > >
> > > There is a development in the parse_integer in the lib/. I reviewed that series
> > > and hopefully it will go in. With that done, we better reuse the lib/ function.
> > >
> > > https://lore.kernel.org/linux-hardening/20260202115451.290173-1-dmantipov@yandex.ru/
> >
> > In this patch, I see that it updates the overflow check, but I am not
> > seeing that function being exposed to other kernel modules.
>
> Can the IIO be compiled as a module? If so, then we would need to export that
> function. (Note, we may export only for the exact module(s) in question, so
> nobody else will be able to use it. See EXPORT_SYMBOL_FOR_MODULES() macro.)
Yes, one can have an industrialio.ko.
Then, would it be fine to use:
EXPORT_SYMBOL_FOR_MODULES(_parse_integer_limit, "industrialio");
in lib/kstrtox.c; and:
#include "../../lib/kstrtox.h"
in drivers/iio/industrialio-core.c
that does not look pretty.
--
Kind regards,
Rodrigo Alencar
On Wed, Feb 04, 2026 at 10:28:05AM +0000, Rodrigo Alencar wrote: > On 26/02/04 11:57AM, Andy Shevchenko wrote: > > On Wed, Feb 04, 2026 at 09:42:55AM +0000, Rodrigo Alencar wrote: > > > On 26/02/04 03:45AM, Andy Shevchenko wrote: > > > > On Fri, Jan 30, 2026 at 10:06:43AM +0000, Rodrigo Alencar via B4 Relay wrote: ... > > > > There is a development in the parse_integer in the lib/. I reviewed that series > > > > and hopefully it will go in. With that done, we better reuse the lib/ function. > > > > > > > > https://lore.kernel.org/linux-hardening/20260202115451.290173-1-dmantipov@yandex.ru/ > > > > > > In this patch, I see that it updates the overflow check, but I am not > > > seeing that function being exposed to other kernel modules. > > > > Can the IIO be compiled as a module? If so, then we would need to export that > > function. (Note, we may export only for the exact module(s) in question, so > > nobody else will be able to use it. See EXPORT_SYMBOL_FOR_MODULES() macro.) > > Yes, one can have an industrialio.ko. > Then, would it be fine to use: > > EXPORT_SYMBOL_FOR_MODULES(_parse_integer_limit, "industrialio"); > > in lib/kstrtox.c; and: > > #include "../../lib/kstrtox.h" > > in drivers/iio/industrialio-core.c > > that does not look pretty. Yeah, but I think it's fine as long as we have an associated FIXME. In any case Jonathan is the one who makes a decision here. -- With Best Regards, Andy Shevchenko
On Wed, 4 Feb 2026 12:34:52 +0200 Andy Shevchenko <andriy.shevchenko@intel.com> wrote: > On Wed, Feb 04, 2026 at 10:28:05AM +0000, Rodrigo Alencar wrote: > > On 26/02/04 11:57AM, Andy Shevchenko wrote: > > > On Wed, Feb 04, 2026 at 09:42:55AM +0000, Rodrigo Alencar wrote: > > > > On 26/02/04 03:45AM, Andy Shevchenko wrote: > > > > > On Fri, Jan 30, 2026 at 10:06:43AM +0000, Rodrigo Alencar via B4 Relay wrote: > > ... > > > > > > There is a development in the parse_integer in the lib/. I reviewed that series > > > > > and hopefully it will go in. With that done, we better reuse the lib/ function. > > > > > > > > > > https://lore.kernel.org/linux-hardening/20260202115451.290173-1-dmantipov@yandex.ru/ > > > > > > > > In this patch, I see that it updates the overflow check, but I am not > > > > seeing that function being exposed to other kernel modules. > > > > > > Can the IIO be compiled as a module? If so, then we would need to export that > > > function. (Note, we may export only for the exact module(s) in question, so > > > nobody else will be able to use it. See EXPORT_SYMBOL_FOR_MODULES() macro.) > > > > Yes, one can have an industrialio.ko. > > Then, would it be fine to use: > > > > EXPORT_SYMBOL_FOR_MODULES(_parse_integer_limit, "industrialio"); > > > > in lib/kstrtox.c; and: > > > > #include "../../lib/kstrtox.h" > > > > in drivers/iio/industrialio-core.c > > > > that does not look pretty. > > Yeah, but I think it's fine as long as we have an associated FIXME. > In any case Jonathan is the one who makes a decision here. You've lost me. Why do we need to restrict this function to use by specific modules? We normally only bother with that dance when there is a big footgun or something deep in core kernel code where we want to be very careful who uses it. To me it doesn't seem appropriate here. Jonathan >
On Sat, Feb 07, 2026 at 05:02:28PM +0000, Jonathan Cameron wrote: > On Wed, 4 Feb 2026 12:34:52 +0200 > Andy Shevchenko <andriy.shevchenko@intel.com> wrote: > > On Wed, Feb 04, 2026 at 10:28:05AM +0000, Rodrigo Alencar wrote: > > > On 26/02/04 11:57AM, Andy Shevchenko wrote: > > > > On Wed, Feb 04, 2026 at 09:42:55AM +0000, Rodrigo Alencar wrote: > > > > > On 26/02/04 03:45AM, Andy Shevchenko wrote: > > > > > > On Fri, Jan 30, 2026 at 10:06:43AM +0000, Rodrigo Alencar via B4 Relay wrote: ... > > > > > > There is a development in the parse_integer in the lib/. I reviewed that series > > > > > > and hopefully it will go in. With that done, we better reuse the lib/ function. > > > > > > > > > > > > https://lore.kernel.org/linux-hardening/20260202115451.290173-1-dmantipov@yandex.ru/ > > > > > > > > > > In this patch, I see that it updates the overflow check, but I am not > > > > > seeing that function being exposed to other kernel modules. > > > > > > > > Can the IIO be compiled as a module? If so, then we would need to export that > > > > function. (Note, we may export only for the exact module(s) in question, so > > > > nobody else will be able to use it. See EXPORT_SYMBOL_FOR_MODULES() macro.) > > > > > > Yes, one can have an industrialio.ko. > > > Then, would it be fine to use: > > > > > > EXPORT_SYMBOL_FOR_MODULES(_parse_integer_limit, "industrialio"); > > > > > > in lib/kstrtox.c; and: > > > > > > #include "../../lib/kstrtox.h" > > > > > > in drivers/iio/industrialio-core.c > > > > > > that does not look pretty. > > > > Yeah, but I think it's fine as long as we have an associated FIXME. > > In any case Jonathan is the one who makes a decision here. > > You've lost me. Why do we need to restrict this function to use > by specific modules? Because it should not be used outside the lib/* to begin with. The mentioned series relaxed that a bit, but the idea behind is still the same. We rather should have a front-end wrappers like safe_strtox() or so to do the job, not the parse_integer*() one(s). > We normally only bother with that dance when > there is a big footgun or something deep in core kernel code where > we want to be very careful who uses it. That's exactly the case. > To me it doesn't seem appropriate here. -- With Best Regards, Andy Shevchenko
On Fri, 2026-01-30 at 10:06 +0000, Rodrigo Alencar via B4 Relay wrote:
> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
>
> Add iio_str_to_fixpoint64() function that leverages simple_strtoull()
> to parse numbers from a string.
> A helper function __iio_str_to_fixpoint64() replaces
> __iio_str_to_fixpoint() implementation, extending its usage for
> 64-bit fixed-point parsing.
>
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
> ---
> drivers/iio/industrialio-core.c | 211 ++++++++++++++++++++++++++++++----------
> include/linux/iio/iio.h | 2 +
> 2 files changed, 163 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 3115d59c1372..37e9ed6b659b 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -21,6 +21,7 @@
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/mutex.h>
> +#include <linux/overflow.h>
> #include <linux/poll.h>
> #include <linux/property.h>
> #include <linux/sched.h>
> @@ -881,6 +882,136 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
> }
> }
>
> +/**
> + * iio_safe_strntou64() - Parse u64 from string checking for overflow safety
> + * @str: The string to parse
> + * @endp: output pointer to the end parsing position
> + * @result: parsed value
> + * @max_chars: maximum number of digit characters to read
> + *
> + * This function is used in fixed-point parsing and it iterates over a const
> + * char array. It might duplicate behavior of simple_strtoull() or kstrtoull(),
> + * but those have their own limitations:
> + * - simple_strtoull() is not overflow-safe and its usage is discouraged;
> + * - kstrtoull() is safe, but requires termination and it would required a copy
> + * of the string to a temporary buffer.
> + *
> + * The implementation of this function is similar to _parse_integer_limit()
> + * available in lib/kstrtox.h, but that header/function is not available to be
> + * used in kernel modules. Hence, this implementation may need to change or
> + * removed to reuse a new suitable helper that is properly exposed.
> + *
> + * Returns:
> + * number of parsed characters on success, -ERANGE on overflow
> + */
> +static ssize_t iio_safe_strntou64(const char *str, const char **endp,
> + u64 *result, size_t max_chars)
> +{
> + u64 digit, acc = 0;
> + ssize_t idx = 0;
> +
> + while (isdigit(str[idx]) && idx < max_chars) {
> + digit = str[idx] - '0';
> + if (unlikely(acc & (~0ull << 60))) {
> + if (check_mul_overflow(acc, 10, &acc) ||
> + check_add_overflow(acc, digit, &acc))
> + return -ERANGE;
> + } else {
> + acc = acc * 10 + digit;
> + }
> + idx++;
> + }
> +
> + *endp = str + idx;
> + *result = acc;
> + return idx;
> +}
> +
> +/**
> + * __iio_str_to_fixpoint64() - Parse a fixed-point number from a string
> + * @str: The string to parse
> + * @fract_mult: Multiplier for the first decimal place, should be a power of 10
> + * @integer: The integer part of the number
> + * @fract: The fractional part of the number
> + * @scale_db: True if this should parse as dB
> + *
> + * This variant uses 64-bit integers for both integer and fractional parts.
> + * Parsed positive values greater than S64_MAX are returned as-is. Parsed
> + * negative values less than S64_MIN are treated as range error, so -ERANGE is
> + * returned.
> + *
> + * Returns:
> + * 0 on success, or a negative error code if the string could not be parsed.
> + */
> +static int __iio_str_to_fixpoint64(const char *str, u64 fract_mult,
> + s64 *integer, s64 *fract, bool scale_db)
> +{
> + u64 i = 0, f = 0;
> + int ret, precision = ffs(fract_mult);
> + bool negative = false;
> +
> + if (precision > 20) /* ceil(log10(U64_MAX)) = 20 */
> + return -EINVAL;
> +
> + if (str[0] == '-') {
> + negative = true;
> + str++;
> + } else if (str[0] == '+') {
> + str++;
> + }
> +
> + ret = iio_safe_strntou64(str, &str, &i, SIZE_MAX);
> + if (ret < 0)
> + return ret;
> +
> + if (precision && *str == '.') {
> + str++; /* skip decimal point */
> + ret = iio_safe_strntou64(str, &str, &f, precision);
> + if (ret < 0)
> + return ret;
> +
> + if (ret < precision) /* scale up */
> + f *= int_pow(10, precision - ret);
> +
> + while (isdigit(*str)) /* truncate: ignore further digits */
> + str++;
> + }
> +
> + if (!ret)
> + return -EINVAL;
> +
> + if (scale_db) {
> + /* Ignore the dB suffix */
> + if (!strncmp(str, " dB", sizeof(" dB") - 1))
> + str += sizeof(" dB") - 1;
> + else if (!strncmp(str, "dB", sizeof("dB") - 1))
> + str += sizeof("dB") - 1;
> + }
> +
> + if (*str == '\n')
> + str++;
> +
> + if (*str != '\0')
> + return -EINVAL;
> +
> + if (negative) {
> + if (i) {
> + if (i > (u64)S64_MIN)
> + return -ERANGE;
> + i = -i;
> + } else {
> + if (f > (u64)S64_MIN)
> + return -ERANGE;
> + f = -f;
> + }
> + }
> +
> + *integer = i;
> + *fract = f;
> +
> + return 0;
> +}
> +
> /**
> * __iio_str_to_fixpoint() - Parse a fixed-point number from a string
> * @str: The string to parse
> @@ -895,63 +1026,43 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
> static int __iio_str_to_fixpoint(const char *str, int fract_mult,
> int *integer, int *fract, bool scale_db)
> {
> - int i = 0, f = 0;
> - bool integer_part = true, negative = false;
> + s64 integer64, fract64;
> + int ret;
>
> - if (fract_mult == 0) {
> - *fract = 0;
> + ret = __iio_str_to_fixpoint64(str, fract_mult, &integer64, &fract64,
> + scale_db);
> + if (ret)
> + return ret;
I know it feels tempting to do the above while adding the 64bit variant. But isn't the
overflow safety also an issue on the 32bit variant? IMO, we should first have a patch
adding the overflow safety with a Fixes tag and then add 64bit support.
>
> - return kstrtoint(str, 0, integer);
> - }
> + if (integer64 < INT_MIN || integer64 > UINT_MAX ||
> + fract64 < INT_MIN || fract64 > UINT_MAX)
> + return -ERANGE;
>
> - if (str[0] == '-') {
> - negative = true;
> - str++;
> - } else if (str[0] == '+') {
> - str++;
> - }
> -
> - while (*str) {
> - if ('0' <= *str && *str <= '9') {
> - if (integer_part) {
> - i = i * 10 + *str - '0';
> - } else {
> - f += fract_mult * (*str - '0');
> - fract_mult /= 10;
> - }
> - } else if (*str == '\n') {
> - if (*(str + 1) == '\0')
> - break;
> - return -EINVAL;
> - } else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) {
> - /* Ignore the dB suffix */
> - str += sizeof(" dB") - 1;
> - continue;
> - } else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) {
> - /* Ignore the dB suffix */
> - str += sizeof("dB") - 1;
> - continue;
> - } else if (*str == '.' && integer_part) {
> - integer_part = false;
> - } else {
> - return -EINVAL;
> - }
> - str++;
> - }
> -
> - if (negative) {
> - if (i)
> - i = -i;
> - else
> - f = -f;
> - }
> -
> - *integer = i;
> - *fract = f;
> + *integer = integer64;
> + *fract = fract64;
Hmmm, aren't we truncating the values? They are still int pointers...
- Nuno Sá
On 26/02/02 09:57AM, Nuno Sá wrote:
> On Fri, 2026-01-30 at 10:06 +0000, Rodrigo Alencar via B4 Relay wrote:
> > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> >
> > Add iio_str_to_fixpoint64() function that leverages simple_strtoull()
> > to parse numbers from a string.
> > A helper function __iio_str_to_fixpoint64() replaces
> > __iio_str_to_fixpoint() implementation, extending its usage for
> > 64-bit fixed-point parsing.
...
> > /**
> > * __iio_str_to_fixpoint() - Parse a fixed-point number from a string
> > * @str: The string to parse
> > @@ -895,63 +1026,43 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
> > static int __iio_str_to_fixpoint(const char *str, int fract_mult,
> > int *integer, int *fract, bool scale_db)
> > {
> > - int i = 0, f = 0;
> > - bool integer_part = true, negative = false;
> > + s64 integer64, fract64;
> > + int ret;
> >
> > - if (fract_mult == 0) {
> > - *fract = 0;
> > + ret = __iio_str_to_fixpoint64(str, fract_mult, &integer64, &fract64,
> > + scale_db);
> > + if (ret)
> > + return ret;
>
> I know it feels tempting to do the above while adding the 64bit variant. But isn't the
> overflow safety also an issue on the 32bit variant? IMO, we should first have a patch
> adding the overflow safety with a Fixes tag and then add 64bit support.
I think handling 64-bit support after taclking the overflow issue
would require changes on top of previous ones, which might get a messy
commit history, no? Mostly because the 64-bit variant of the function
is being used inside the 32-bit one. Also, the added auxiliary function
that implements the overflow check parses u64, which allowed for the
removal of the while loop in the __iio_str_to_fixpoint() implementation.
>
> >
> > - return kstrtoint(str, 0, integer);
> > - }
> > + if (integer64 < INT_MIN || integer64 > UINT_MAX ||
> > + fract64 < INT_MIN || fract64 > UINT_MAX)
> > + return -ERANGE;
> >
> > - if (str[0] == '-') {
> > - negative = true;
> > - str++;
> > - } else if (str[0] == '+') {
> > - str++;
> > - }
> > -
> > - while (*str) {
> > - if ('0' <= *str && *str <= '9') {
> > - if (integer_part) {
> > - i = i * 10 + *str - '0';
> > - } else {
> > - f += fract_mult * (*str - '0');
> > - fract_mult /= 10;
> > - }
> > - } else if (*str == '\n') {
> > - if (*(str + 1) == '\0')
> > - break;
> > - return -EINVAL;
> > - } else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) {
> > - /* Ignore the dB suffix */
> > - str += sizeof(" dB") - 1;
> > - continue;
> > - } else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) {
> > - /* Ignore the dB suffix */
> > - str += sizeof("dB") - 1;
> > - continue;
> > - } else if (*str == '.' && integer_part) {
> > - integer_part = false;
> > - } else {
> > - return -EINVAL;
> > - }
> > - str++;
> > - }
> > -
> > - if (negative) {
> > - if (i)
> > - i = -i;
> > - else
> > - f = -f;
> > - }
> > -
> > - *integer = i;
> > - *fract = f;
> > + *integer = integer64;
> > + *fract = fract64;
>
> Hmmm, aren't we truncating the values? They are still int pointers...
Yes, truncation happens here. integer64 and fract64 are range checked
before this assignment.
--
Kind regards,
Rodrigo Alencar
On Tue, 2026-02-03 at 09:26 +0000, Rodrigo Alencar wrote:
> On 26/02/02 09:57AM, Nuno Sá wrote:
> > On Fri, 2026-01-30 at 10:06 +0000, Rodrigo Alencar via B4 Relay wrote:
> > > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > >
> > > Add iio_str_to_fixpoint64() function that leverages simple_strtoull()
> > > to parse numbers from a string.
> > > A helper function __iio_str_to_fixpoint64() replaces
> > > __iio_str_to_fixpoint() implementation, extending its usage for
> > > 64-bit fixed-point parsing.
>
> ...
>
> > > /**
> > > * __iio_str_to_fixpoint() - Parse a fixed-point number from a string
> > > * @str: The string to parse
> > > @@ -895,63 +1026,43 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
> > > static int __iio_str_to_fixpoint(const char *str, int fract_mult,
> > > int *integer, int *fract, bool scale_db)
> > > {
> > > - int i = 0, f = 0;
> > > - bool integer_part = true, negative = false;
> > > + s64 integer64, fract64;
> > > + int ret;
> > >
> > > - if (fract_mult == 0) {
> > > - *fract = 0;
> > > + ret = __iio_str_to_fixpoint64(str, fract_mult, &integer64, &fract64,
> > > + scale_db);
> > > + if (ret)
> > > + return ret;
> >
> > I know it feels tempting to do the above while adding the 64bit variant. But isn't the
> > overflow safety also an issue on the 32bit variant? IMO, we should first have a patch
> > adding the overflow safety with a Fixes tag and then add 64bit support.
>
> I think handling 64-bit support after taclking the overflow issue
> would require changes on top of previous ones, which might get a messy
> commit history, no? Mostly because the 64-bit variant of the function
> is being used inside the 32-bit one. Also, the added auxiliary function
> that implements the overflow check parses u64, which allowed for the
> removal of the while loop in the __iio_str_to_fixpoint() implementation.
Typically we do fixes before because we might want to backport them and we just want to backport the
fix (so not the 64bit support). But we never really had any known issues with the current API
(AFAIK) so it might be ok as-is. Will defer to Jonathan.
- Nuno Sá
> before this assignment.
>
On Tue, 03 Feb 2026 10:04:01 +0000
Nuno Sá <noname.nuno@gmail.com> wrote:
> On Tue, 2026-02-03 at 09:26 +0000, Rodrigo Alencar wrote:
> > On 26/02/02 09:57AM, Nuno Sá wrote:
> > > On Fri, 2026-01-30 at 10:06 +0000, Rodrigo Alencar via B4 Relay wrote:
> > > > From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> > > >
> > > > Add iio_str_to_fixpoint64() function that leverages simple_strtoull()
> > > > to parse numbers from a string.
> > > > A helper function __iio_str_to_fixpoint64() replaces
> > > > __iio_str_to_fixpoint() implementation, extending its usage for
> > > > 64-bit fixed-point parsing.
> >
> > ...
> >
> > > > /**
> > > > * __iio_str_to_fixpoint() - Parse a fixed-point number from a string
> > > > * @str: The string to parse
> > > > @@ -895,63 +1026,43 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
> > > > static int __iio_str_to_fixpoint(const char *str, int fract_mult,
> > > > int *integer, int *fract, bool scale_db)
> > > > {
> > > > - int i = 0, f = 0;
> > > > - bool integer_part = true, negative = false;
> > > > + s64 integer64, fract64;
> > > > + int ret;
> > > >
> > > > - if (fract_mult == 0) {
> > > > - *fract = 0;
> > > > + ret = __iio_str_to_fixpoint64(str, fract_mult, &integer64, &fract64,
> > > > + scale_db);
> > > > + if (ret)
> > > > + return ret;
> > >
> > > I know it feels tempting to do the above while adding the 64bit variant. But isn't the
> > > overflow safety also an issue on the 32bit variant? IMO, we should first have a patch
> > > adding the overflow safety with a Fixes tag and then add 64bit support.
> >
> > I think handling 64-bit support after taclking the overflow issue
> > would require changes on top of previous ones, which might get a messy
> > commit history, no? Mostly because the 64-bit variant of the function
> > is being used inside the 32-bit one. Also, the added auxiliary function
> > that implements the overflow check parses u64, which allowed for the
> > removal of the while loop in the __iio_str_to_fixpoint() implementation.
>
> Typically we do fixes before because we might want to backport them and we just want to backport the
> fix (so not the 64bit support). But we never really had any known issues with the current API
> (AFAIK) so it might be ok as-is. Will defer to Jonathan.
When we say overflow, I assume we just get the wrong value? If so
then I doubt anyone ever noticed it. Worth tidying up as a useability
improvement but not sure it's worth back porting.
Jonathan
>
> - Nuno Sá
> > before this assignment.
> >
© 2016 - 2026 Red Hat, Inc.