diff mbox series

block/curl: use strlen instead of strchr

Message ID 20240628054942.657397-1-vsementsov@yandex-team.ru
State New
Headers show
Series block/curl: use strlen instead of strchr | expand

Commit Message

Vladimir Sementsov-Ogievskiy June 28, 2024, 5:49 a.m. UTC
We already know where colon is, so no reason to search for it. Also,
avoid a code, which looks like we forget to check return value of
strchr() to NULL.

Suggested-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---

This replaces my patch
  [PATCH] block/curl: explicitly assert that strchr returns non-NULL value
Supersedes: <20240627153059.589070-1-vsementsov@yandex-team.ru>

 block/curl.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Michael Tokarev June 29, 2024, 6:20 a.m. UTC | #1
On 6/28/24 08:49, Vladimir Sementsov-Ogievskiy wrote:
> We already know where colon is, so no reason to search for it. Also,
> avoid a code, which looks like we forget to check return value of
> strchr() to NULL.
> 
> Suggested-by: Kevin Wolf <kwolf@redhat.com>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> ---
> 
> This replaces my patch
>    [PATCH] block/curl: explicitly assert that strchr returns non-NULL value
> Supersedes: <20240627153059.589070-1-vsementsov@yandex-team.ru>
> 
>   block/curl.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/block/curl.c b/block/curl.c
> index 419f7c89ef..d03bfe4817 100644
> --- a/block/curl.c
> +++ b/block/curl.c
> @@ -219,7 +219,7 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
>           && g_ascii_strncasecmp(header, accept_ranges,
>                                  strlen(accept_ranges)) == 0) {
>   
> -        char *p = strchr(header, ':') + 1;
> +        char *p = header + strlen(accept_ranges);
>   
>           /* Skip whitespace between the header name and value. */
>           while (p < end && *p && g_ascii_isspace(*p)) {

Heck.  All these strlen()s look ugly, especially in the
loop iterations..  To my taste anyway.

How about this instead:

diff --git a/block/curl.c b/block/curl.c
index 419f7c89ef..5e91807115 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -210,35 +210,34 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
  {
      BDRVCURLState *s = opaque;
      size_t realsize = size * nmemb;
-    const char *header = (char *)ptr;
-    const char *end = header + realsize;
-    const char *accept_ranges = "accept-ranges:";
-    const char *bytes = "bytes";
+    const char *p = (char *)ptr;
+    const char *end = p + realsize;
+    const char *t;

-    if (realsize >= strlen(accept_ranges)
-        && g_ascii_strncasecmp(header, accept_ranges,
-                               strlen(accept_ranges)) == 0) {
+    for (t = "accept-ranges:"; p < end && *t; ++p, ++t) {
+        if (g_ascii_tolower(*p) != *t) {
+            return realsize;
+        }
+    }

-        char *p = strchr(header, ':') + 1;
+    /* Skip whitespace between the header name and value. */
+    while (p < end && *p && g_ascii_isspace(*p)) {
+        p++;
+    }

-        /* Skip whitespace between the header name and value. */
-        while (p < end && *p && g_ascii_isspace(*p)) {
-            p++;
+    for (t = "bytes"; p < end && *t; ++p, ++t) {
+        if (g_ascii_tolower(*p) != *t) {
+            return realsize;
          }
+    }

-        if (end - p >= strlen(bytes)
-            && strncmp(p, bytes, strlen(bytes)) == 0) {
-
-            /* Check that there is nothing but whitespace after the value. */
-            p += strlen(bytes);
-            while (p < end && *p && g_ascii_isspace(*p)) {
-                p++;
-            }
+    /* Check that there is nothing but whitespace after the value. */
+    while (p < end && *p && g_ascii_isspace(*p)) {
+        p++;
+    }

-            if (p == end || !*p) {
-                s->accept_range = true;
-            }
-        }
+    if (p == end || !*p) {
+        s->accept_range = true;
      }

      return realsize;


Whole function for easy read:


/* Called from curl_multi_do_locked, with s->mutex held.  */
static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
{
     BDRVCURLState *s = opaque;
     size_t realsize = size * nmemb;
     const char *p = (char *)ptr;
     const char *end = p + realsize;
     const char *t;

     for (t = "accept-ranges:"; p < end && *t; ++p, ++t) {
         if (g_ascii_tolower(*p) != *t) {
             return realsize;
         }
     }

     /* Skip whitespace between the header name and value. */
     while (p < end && *p && g_ascii_isspace(*p)) {
         p++;
     }

     for (t = "bytes"; p < end && *t; ++p, ++t) {
         if (g_ascii_tolower(*p) != *t) {
             return realsize;
         }
     }

     /* Check that there is nothing but whitespace after the value. */
     while (p < end && *p && g_ascii_isspace(*p)) {
         p++;
     }

     if (p == end || !*p) {
         s->accept_range = true;
     }

     return realsize;
}

Dunno why it also checks for *p being !=0 in the last part, but ok.

Sorry for the noise.  But I dislike usage of strlen() etc like this :)

/mjt
Michael Tokarev June 29, 2024, 6:36 a.m. UTC | #2
On 6/29/24 09:20, Michael Tokarev wrote:
> On 6/28/24 08:49, Vladimir Sementsov-Ogievskiy wrote:
>> We already know where colon is, so no reason to search for it. Also,
>> avoid a code, which looks like we forget to check return value of
>> strchr() to NULL.
>>
>> Suggested-by: Kevin Wolf <kwolf@redhat.com>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>> ---
>>
>> This replaces my patch
>>    [PATCH] block/curl: explicitly assert that strchr returns non-NULL value
>> Supersedes: <20240627153059.589070-1-vsementsov@yandex-team.ru>
>>
>>   block/curl.c | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/block/curl.c b/block/curl.c
>> index 419f7c89ef..d03bfe4817 100644
>> --- a/block/curl.c
>> +++ b/block/curl.c
>> @@ -219,7 +219,7 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
>>           && g_ascii_strncasecmp(header, accept_ranges,
>>                                  strlen(accept_ranges)) == 0) {
>> -        char *p = strchr(header, ':') + 1;
>> +        char *p = header + strlen(accept_ranges);
>>           /* Skip whitespace between the header name and value. */
>>           while (p < end && *p && g_ascii_isspace(*p)) {
> 
> Heck.  All these strlen()s look ugly, especially in the
> loop iterations..  To my taste anyway.
> 
> How about this instead:

Or even this:

diff --git a/block/curl.c b/block/curl.c
index 419f7c89ef..982597ea7f 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -210,37 +210,28 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
  {
      BDRVCURLState *s = opaque;
      size_t realsize = size * nmemb;
-    const char *header = (char *)ptr;
-    const char *end = header + realsize;
-    const char *accept_ranges = "accept-ranges:";
-    const char *bytes = "bytes";
-
-    if (realsize >= strlen(accept_ranges)
-        && g_ascii_strncasecmp(header, accept_ranges,
-                               strlen(accept_ranges)) == 0) {
-
-        char *p = strchr(header, ':') + 1;
-
-        /* Skip whitespace between the header name and value. */
-        while (p < end && *p && g_ascii_isspace(*p)) {
-            p++;
-        }
-
-        if (end - p >= strlen(bytes)
-            && strncmp(p, bytes, strlen(bytes)) == 0) {
-
-            /* Check that there is nothing but whitespace after the value. */
-            p += strlen(bytes);
-            while (p < end && *p && g_ascii_isspace(*p)) {
-                p++;
-            }
-
-            if (p == end || !*p) {
-                s->accept_range = true;
+    const char *p = (char *)ptr;
+    const char *end = p + realsize;
+    const char *t = "accept-ranges : bytes ";
+
+    while (p < end && *t) {
+        if (*t == ' ') {
+            if (g_ascii_isspace(*p)) {
+                ++p;
+            } else {
+                ++t;
              }
+        } else if (*t == g_ascii_tolower(*p)) {
+            ++p, ++t;
+        } else {
+            break;
          }
      }

+    if (p == end || !*p) {
+        s->accept_range = true;
+    }
+
      return realsize;
  }

Whole thing:

static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
{
     BDRVCURLState *s = opaque;
     size_t realsize = size * nmemb;
     const char *p = (char *)ptr;
     const char *end = p + realsize;
     const char *t = "accept-ranges : bytes ";

     while (p < end && *t) {
         if (*t == ' ') {
             if (g_ascii_isspace(*p)) {
                 ++p;
             } else {
                 ++t;
             }
         } else if (*t == g_ascii_tolower(*p)) {
             ++p, ++t;
         } else {
             break;
         }
     }

     if (p == end || !*p) {
         s->accept_range = true;
     }

     return realsize;
}

/mjt
Michael Tokarev June 29, 2024, 6:39 a.m. UTC | #3
On 6/29/24 09:36, Michael Tokarev wrote:
..
> +    while (p < end && *t) {
> +        if (*t == ' ') {
> +            if (g_ascii_isspace(*p)) {
> +                ++p;
> +            } else {
> +                ++t;
>               }
> +        } else if (*t == g_ascii_tolower(*p)) {
> +            ++p, ++t;
> +        } else {
> +            break;
>           }
>       }
> 
> +    if (p == end || !*p) {

        if (!*t && p == end)  ofc.

/mjt
Vladimir Sementsov-Ogievskiy July 1, 2024, 6:34 a.m. UTC | #4
On 29.06.24 09:20, Michael Tokarev wrote:
> On 6/28/24 08:49, Vladimir Sementsov-Ogievskiy wrote:
>> We already know where colon is, so no reason to search for it. Also,
>> avoid a code, which looks like we forget to check return value of
>> strchr() to NULL.
>>
>> Suggested-by: Kevin Wolf <kwolf@redhat.com>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>> ---
>>
>> This replaces my patch
>>    [PATCH] block/curl: explicitly assert that strchr returns non-NULL value
>> Supersedes: <20240627153059.589070-1-vsementsov@yandex-team.ru>
>>
>>   block/curl.c | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/block/curl.c b/block/curl.c
>> index 419f7c89ef..d03bfe4817 100644
>> --- a/block/curl.c
>> +++ b/block/curl.c
>> @@ -219,7 +219,7 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
>>           && g_ascii_strncasecmp(header, accept_ranges,
>>                                  strlen(accept_ranges)) == 0) {
>> -        char *p = strchr(header, ':') + 1;
>> +        char *p = header + strlen(accept_ranges);
>>           /* Skip whitespace between the header name and value. */
>>           while (p < end && *p && g_ascii_isspace(*p)) {
> 
> Heck.  All these strlen()s look ugly, especially in the
> loop iterations..

I expect that strlen of string constant is calculated in compilation time.

My aim was to fix Coverity complain (I don't see this complain in public qemu coverity project, that's why I don't specify CID in commit message), not to rewrite the whole function. So I'd prefer Kevein's suggesting which is both minimal and makes the code obviously correct.. The only simpler thing is to mark the problem false-positive in Coverity.
Vladimir Sementsov-Ogievskiy July 1, 2024, 6:50 a.m. UTC | #5
On 01.07.24 09:34, Vladimir Sementsov-Ogievskiy wrote:
> On 29.06.24 09:20, Michael Tokarev wrote:
>> On 6/28/24 08:49, Vladimir Sementsov-Ogievskiy wrote:
>>> We already know where colon is, so no reason to search for it. Also,
>>> avoid a code, which looks like we forget to check return value of
>>> strchr() to NULL.
>>>
>>> Suggested-by: Kevin Wolf <kwolf@redhat.com>
>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
>>> ---
>>>
>>> This replaces my patch
>>>    [PATCH] block/curl: explicitly assert that strchr returns non-NULL value
>>> Supersedes: <20240627153059.589070-1-vsementsov@yandex-team.ru>
>>>
>>>   block/curl.c | 2 +-
>>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/block/curl.c b/block/curl.c
>>> index 419f7c89ef..d03bfe4817 100644
>>> --- a/block/curl.c
>>> +++ b/block/curl.c
>>> @@ -219,7 +219,7 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
>>>           && g_ascii_strncasecmp(header, accept_ranges,
>>>                                  strlen(accept_ranges)) == 0) {
>>> -        char *p = strchr(header, ':') + 1;
>>> +        char *p = header + strlen(accept_ranges);
>>>           /* Skip whitespace between the header name and value. */
>>>           while (p < end && *p && g_ascii_isspace(*p)) {
>>
>> Heck.  All these strlen()s look ugly, especially in the
>> loop iterations..
> 
> I expect that strlen of string constant is calculated in compilation time.
> 
> My aim was to fix Coverity complain (I don't see this complain in public qemu coverity project, that's why I don't specify CID in commit message), not to rewrite the whole function. So I'd prefer Kevein's suggesting which is both minimal and makes the code obviously correct.. The only simpler thing is to mark the problem false-positive in Coverity.
> 


Upd: I missed that you sent a patch, this changes things. Of course, you code looks nicer than old one.
diff mbox series

Patch

diff --git a/block/curl.c b/block/curl.c
index 419f7c89ef..d03bfe4817 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -219,7 +219,7 @@  static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
         && g_ascii_strncasecmp(header, accept_ranges,
                                strlen(accept_ranges)) == 0) {
 
-        char *p = strchr(header, ':') + 1;
+        char *p = header + strlen(accept_ranges);
 
         /* Skip whitespace between the header name and value. */
         while (p < end && *p && g_ascii_isspace(*p)) {