Re: [PATCH 3/4] bitmap_parselist: rework input string parser

2019-01-09 Thread Yuri Norov
On Wed, Jan 09, 2019 at 06:01:46PM +0200, Andy Shevchenko wrote:
> On Sun, Dec 23, 2018 at 09:44:55AM +, Yuri Norov wrote:
> > The requirement for this rework is to keep the __bitmap_parselist()
> > copy-less and single-pass but make it more readable and maintainable by
> > splitting into logical parts and removing explicit nested cycles and
> > opaque local variables.
> > 
> > __bitmap_parselist() can parse userspace inputs and therefore we cannot
> > use simple_strtoul() to parse numbers.
> 
> I see two issues with this patch:
> - you are using IS_ERR() but ain't utilizing PTR_ERR(), ERR_PTR() ones

OK. Will use them in v2.

> - you remove lines here which you added in the same series.
>
> Second one shows ordering issue of logical changes.

Do you mean this chunk?

> > -   r.start = a;
> > -   r.off = used_size;
> > -   r.grlen = group_size;
> > -   r.end = b;

It's because I split refactoring into 2 parts for the sake of
readability. Patch #2 may be applied independently if #3 will
be considered inappropriate, that's why I ordered it prior to
#3, and it caused the need for removing that lines. I don't
think it's too ugly though, and I'd prefer to keep 2 and 3
separated with the cost of this little mess.

Reversing the order looks tricky at the first glance, but I'm
OK to join #2 and #3 if it will be considered worthy. Would
work both ways.

> > Signed-off-by: Yury Norov 
> > ---
> >  lib/bitmap.c | 247 ++-
> >  1 file changed, 148 insertions(+), 99 deletions(-)
> > 
> > diff --git a/lib/bitmap.c b/lib/bitmap.c
> > index a60fd9723677..edc7068c28a6 100644
> > --- a/lib/bitmap.c
> > +++ b/lib/bitmap.c
> > @@ -513,6 +513,140 @@ static int bitmap_check_region(const struct region *r)
> > return 0;
> >  }
> >  
> > +static long get_char(char *c, const char *str, int is_user)
> > +{
> > +   if (unlikely(is_user))
> > +   return __get_user(*c, str);
> > +
> > +   *c = *str;
> > +   return 0;
> > +}
> > +
> > +static const char *bitmap_getnum(unsigned int *num, const char *str,
> > +   const char *const end, int is_user)
> > +{
> > +   unsigned int n = 0;
> > +   const char *_str;
> > +   long ret;
> > +   char c;
> > +
> > +   for (_str = str, *num = 0; _str <= end; _str++) {
> > +   ret = get_char(, _str, is_user);
> > +   if (ret)
> > +   return (char *)ret;
> > +
> > +   if (!isdigit(c)) {
> > +   if (_str == str)
> > +   return (char *)-EINVAL;
> > +
> > +   goto out;
> > +   }
> > +
> > +   *num = *num * 10 + (c - '0');
> > +   if (*num < n)
> > +   return (char *)-EOVERFLOW;
> > +
> > +   n = *num;
> > +   }
> > +
> > +out:
> > +   return _str;
> > +}
> > +
> > +static const char *bitmap_find_region(const char *str,
> > +   const char *const end, int is_user)
> > +{
> > +   long ret;
> > +   char c;
> > +
> > +   for (; str < end; str++) {
> > +   ret = get_char(, str, is_user);
> > +   if (ret)
> > +   return (char *)ret;
> > +
> > +   /* Unexpected end of line. */
> > +   if (c == 0 || c == '\n')
> > +   return NULL;
> > +
> > +   /*
> > +* The format allows commas and whitespases
> > +* at the beginning of the region, so skip it.
> > +*/
> > +   if (!isspace(c) && c != ',')
> > +   break;
> > +   }
> > +
> > +   return str;
> > +}
> > +
> > +static const char *bitmap_parse_region(struct region *r, const char *str,
> > +const char *const end, int is_user)
> > +{
> > +   long ret;
> > +   char c;
> > +
> > +   str = bitmap_getnum(>start, str, end, is_user);
> > +   if (IS_ERR(str))
> > +   return str;
> > +
> > +   ret = get_char(, str, is_user);
> > +   if (ret)
> > +   return (char *)ret;
> > +
> > +   if (c == 0 || c == '\n') {
> > +   str = end + 1;
> > +   goto no_end;
> > +   }
> > +
> > +   if (isspace(c) || c == ',')
> > +   goto no_end;
> > +
> > +   if (c != '-')
> > +   return (char *)-EINVAL;
> > +
> > +   str = bitmap_getnum(>end, str + 1, end, is_user);
> > +   if (IS_ERR(str))
> > +   return str;
> > +
> > +   ret = get_char(, str, is_user);
> > +   if (ret)
> > +   return (char *)ret;
> > +
> > +   if (c == 0 || c == '\n') {
> > +   str = end + 1;
> > +   goto no_pattern;
> > +   }
> > +
> > +   if (isspace(c) || c == ',')
> > +   goto no_pattern;
> > +
> > +   if (c != ':')
> > +   return (char *)-EINVAL;
> > +
> > +   str = bitmap_getnum(>off, str + 1, end, is_user);
> > +   if (IS_ERR(str))
> > +   return str;
> > +
> > +   ret = get_char(, str, is_user);
> > +   if (ret)
> > +   return (char *)ret;
> > +
> > +   if (c != '/')
> > +   

Re: [PATCH 3/4] bitmap_parselist: rework input string parser

2019-01-09 Thread Andy Shevchenko
On Sun, Dec 23, 2018 at 09:44:55AM +, Yuri Norov wrote:
> The requirement for this rework is to keep the __bitmap_parselist()
> copy-less and single-pass but make it more readable and maintainable by
> splitting into logical parts and removing explicit nested cycles and
> opaque local variables.
> 
> __bitmap_parselist() can parse userspace inputs and therefore we cannot
> use simple_strtoul() to parse numbers.

I see two issues with this patch:
- you are using IS_ERR() but ain't utilizing PTR_ERR(), ERR_PTR() ones
- you remove lines here which you added in the same series.

Second one shows ordering issue of logical changes.

> 
> Signed-off-by: Yury Norov 
> ---
>  lib/bitmap.c | 247 ++-
>  1 file changed, 148 insertions(+), 99 deletions(-)
> 
> diff --git a/lib/bitmap.c b/lib/bitmap.c
> index a60fd9723677..edc7068c28a6 100644
> --- a/lib/bitmap.c
> +++ b/lib/bitmap.c
> @@ -513,6 +513,140 @@ static int bitmap_check_region(const struct region *r)
>   return 0;
>  }
>  
> +static long get_char(char *c, const char *str, int is_user)
> +{
> + if (unlikely(is_user))
> + return __get_user(*c, str);
> +
> + *c = *str;
> + return 0;
> +}
> +
> +static const char *bitmap_getnum(unsigned int *num, const char *str,
> + const char *const end, int is_user)
> +{
> + unsigned int n = 0;
> + const char *_str;
> + long ret;
> + char c;
> +
> + for (_str = str, *num = 0; _str <= end; _str++) {
> + ret = get_char(, _str, is_user);
> + if (ret)
> + return (char *)ret;
> +
> + if (!isdigit(c)) {
> + if (_str == str)
> + return (char *)-EINVAL;
> +
> + goto out;
> + }
> +
> + *num = *num * 10 + (c - '0');
> + if (*num < n)
> + return (char *)-EOVERFLOW;
> +
> + n = *num;
> + }
> +
> +out:
> + return _str;
> +}
> +
> +static const char *bitmap_find_region(const char *str,
> + const char *const end, int is_user)
> +{
> + long ret;
> + char c;
> +
> + for (; str < end; str++) {
> + ret = get_char(, str, is_user);
> + if (ret)
> + return (char *)ret;
> +
> + /* Unexpected end of line. */
> + if (c == 0 || c == '\n')
> + return NULL;
> +
> + /*
> +  * The format allows commas and whitespases
> +  * at the beginning of the region, so skip it.
> +  */
> + if (!isspace(c) && c != ',')
> + break;
> + }
> +
> + return str;
> +}
> +
> +static const char *bitmap_parse_region(struct region *r, const char *str,
> +  const char *const end, int is_user)
> +{
> + long ret;
> + char c;
> +
> + str = bitmap_getnum(>start, str, end, is_user);
> + if (IS_ERR(str))
> + return str;
> +
> + ret = get_char(, str, is_user);
> + if (ret)
> + return (char *)ret;
> +
> + if (c == 0 || c == '\n') {
> + str = end + 1;
> + goto no_end;
> + }
> +
> + if (isspace(c) || c == ',')
> + goto no_end;
> +
> + if (c != '-')
> + return (char *)-EINVAL;
> +
> + str = bitmap_getnum(>end, str + 1, end, is_user);
> + if (IS_ERR(str))
> + return str;
> +
> + ret = get_char(, str, is_user);
> + if (ret)
> + return (char *)ret;
> +
> + if (c == 0 || c == '\n') {
> + str = end + 1;
> + goto no_pattern;
> + }
> +
> + if (isspace(c) || c == ',')
> + goto no_pattern;
> +
> + if (c != ':')
> + return (char *)-EINVAL;
> +
> + str = bitmap_getnum(>off, str + 1, end, is_user);
> + if (IS_ERR(str))
> + return str;
> +
> + ret = get_char(, str, is_user);
> + if (ret)
> + return (char *)ret;
> +
> + if (c != '/')
> + return (char *)-EINVAL;
> +
> + str = bitmap_getnum(>grlen, str + 1, end, is_user);
> +
> + return str;
> +
> +no_end:
> + r->end = r->start;
> +no_pattern:
> + r->off = r->end + 1;
> + r->grlen = r->end + 1;
> +
> + return (char *)str;
> +}
> +
>  /**
>   * __bitmap_parselist - convert list format ASCII string to bitmap
>   * @buf: read nul-terminated user string from this buffer
> @@ -534,113 +668,28 @@ static int bitmap_check_region(const struct region *r)
>   *
>   * Returns: 0 on success, -errno on invalid input strings. Error values:
>   *
> - *   - ``-EINVAL``: second number in range smaller than first
> + *   - ``-EINVAL``: wrong region format
>   *   - ``-EINVAL``: invalid character in string
>   *   - ``-ERANGE``: bit number specified too large for mask
> + *   - ``-EOVERFLOW``: integer overflow in the input parameters
>  

Re: [PATCH 3/4] bitmap_parselist: rework input string parser

2018-12-23 Thread kbuild test robot
Hi Yuri,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linus/master]
[also build test WARNING on v4.20-rc7 next-20181221]
[if your patch is applied to the wrong git tree, please drop us a note to help 
improve the system]

url:
https://github.com/0day-ci/linux/commits/Yuri-Norov/rework-bitmap_parselist/20181223-175529
reproduce: make htmldocs

All warnings (new ones prefixed by >>):

   WARNING: convert(1) not found, for SVG to PDF conversion install ImageMagick 
(https://www.imagemagick.org)
   lib/bitmap.c:679: warning: Excess function parameter 'buflen' description in 
'__bitmap_parselist'
   lib/bitmap.c:680: warning: Excess function parameter 'buflen' description in 
'__bitmap_parselist'
>> lib/bitmap.c:680: warning: Function parameter or member 'end' not described 
>> in '__bitmap_parselist'
   lib/bitmap.c:680: warning: Excess function parameter 'buflen' description in 
'__bitmap_parselist'
   include/linux/rcutree.h:1: warning: no structured comments found
   kernel/rcu/tree.c:684: warning: Excess function parameter 'irq' description 
in 'rcu_nmi_exit'
   include/linux/srcu.h:175: warning: Function parameter or member 'p' not 
described in 'srcu_dereference_notrace'
   include/linux/srcu.h:175: warning: Function parameter or member 'sp' not 
described in 'srcu_dereference_notrace'
   include/linux/gfp.h:1: warning: no structured comments found
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.ibss' not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.connect' not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.keys' not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 'wext.ie' 
not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.ie_len' not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.bssid' not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.ssid' not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.default_key' not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.default_mgmt_key' not described in 'wireless_dev'
   include/net/cfg80211.h:4439: warning: Function parameter or member 
'wext.prev_bssid_valid' not described in 'wireless_dev'
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   include/net/cfg80211.h:2838: warning: cannot understand function prototype: 
'struct cfg80211_ftm_responder_stats '
   

[PATCH 3/4] bitmap_parselist: rework input string parser

2018-12-23 Thread Yuri Norov
The requirement for this rework is to keep the __bitmap_parselist()
copy-less and single-pass but make it more readable and maintainable by
splitting into logical parts and removing explicit nested cycles and
opaque local variables.

__bitmap_parselist() can parse userspace inputs and therefore we cannot
use simple_strtoul() to parse numbers.

Signed-off-by: Yury Norov 
---
 lib/bitmap.c | 247 ++-
 1 file changed, 148 insertions(+), 99 deletions(-)

diff --git a/lib/bitmap.c b/lib/bitmap.c
index a60fd9723677..edc7068c28a6 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -513,6 +513,140 @@ static int bitmap_check_region(const struct region *r)
return 0;
 }
 
+static long get_char(char *c, const char *str, int is_user)
+{
+   if (unlikely(is_user))
+   return __get_user(*c, str);
+
+   *c = *str;
+   return 0;
+}
+
+static const char *bitmap_getnum(unsigned int *num, const char *str,
+   const char *const end, int is_user)
+{
+   unsigned int n = 0;
+   const char *_str;
+   long ret;
+   char c;
+
+   for (_str = str, *num = 0; _str <= end; _str++) {
+   ret = get_char(, _str, is_user);
+   if (ret)
+   return (char *)ret;
+
+   if (!isdigit(c)) {
+   if (_str == str)
+   return (char *)-EINVAL;
+
+   goto out;
+   }
+
+   *num = *num * 10 + (c - '0');
+   if (*num < n)
+   return (char *)-EOVERFLOW;
+
+   n = *num;
+   }
+
+out:
+   return _str;
+}
+
+static const char *bitmap_find_region(const char *str,
+   const char *const end, int is_user)
+{
+   long ret;
+   char c;
+
+   for (; str < end; str++) {
+   ret = get_char(, str, is_user);
+   if (ret)
+   return (char *)ret;
+
+   /* Unexpected end of line. */
+   if (c == 0 || c == '\n')
+   return NULL;
+
+   /*
+* The format allows commas and whitespases
+* at the beginning of the region, so skip it.
+*/
+   if (!isspace(c) && c != ',')
+   break;
+   }
+
+   return str;
+}
+
+static const char *bitmap_parse_region(struct region *r, const char *str,
+const char *const end, int is_user)
+{
+   long ret;
+   char c;
+
+   str = bitmap_getnum(>start, str, end, is_user);
+   if (IS_ERR(str))
+   return str;
+
+   ret = get_char(, str, is_user);
+   if (ret)
+   return (char *)ret;
+
+   if (c == 0 || c == '\n') {
+   str = end + 1;
+   goto no_end;
+   }
+
+   if (isspace(c) || c == ',')
+   goto no_end;
+
+   if (c != '-')
+   return (char *)-EINVAL;
+
+   str = bitmap_getnum(>end, str + 1, end, is_user);
+   if (IS_ERR(str))
+   return str;
+
+   ret = get_char(, str, is_user);
+   if (ret)
+   return (char *)ret;
+
+   if (c == 0 || c == '\n') {
+   str = end + 1;
+   goto no_pattern;
+   }
+
+   if (isspace(c) || c == ',')
+   goto no_pattern;
+
+   if (c != ':')
+   return (char *)-EINVAL;
+
+   str = bitmap_getnum(>off, str + 1, end, is_user);
+   if (IS_ERR(str))
+   return str;
+
+   ret = get_char(, str, is_user);
+   if (ret)
+   return (char *)ret;
+
+   if (c != '/')
+   return (char *)-EINVAL;
+
+   str = bitmap_getnum(>grlen, str + 1, end, is_user);
+
+   return str;
+
+no_end:
+   r->end = r->start;
+no_pattern:
+   r->off = r->end + 1;
+   r->grlen = r->end + 1;
+
+   return (char *)str;
+}
+
 /**
  * __bitmap_parselist - convert list format ASCII string to bitmap
  * @buf: read nul-terminated user string from this buffer
@@ -534,113 +668,28 @@ static int bitmap_check_region(const struct region *r)
  *
  * Returns: 0 on success, -errno on invalid input strings. Error values:
  *
- *   - ``-EINVAL``: second number in range smaller than first
+ *   - ``-EINVAL``: wrong region format
  *   - ``-EINVAL``: invalid character in string
  *   - ``-ERANGE``: bit number specified too large for mask
+ *   - ``-EOVERFLOW``: integer overflow in the input parameters
  */
-static int __bitmap_parselist(const char *buf, unsigned int buflen,
+static int __bitmap_parselist(const char *buf, const char *const end,
int is_user, unsigned long *maskp,
int nmaskbits)
 {
-   unsigned int a, b, old_a, old_b;
-   unsigned int group_size, used_size;
-   int c, old_c, totaldigits, ndigits;
-   const char __user __force *ubuf = (const char __user __force *)buf;
-