diff mbox series

[v2,07/22] qemu-nbd: Avoid strtol open-coding

Message ID 20181215135324.152629-8-eblake@redhat.com (mailing list archive)
State New, archived
Headers show
Series nbd: add qemu-nbd --list | expand

Commit Message

Eric Blake Dec. 15, 2018, 1:53 p.m. UTC
Our copy-and-pasted open-coding of strtol handling forgot to
handle overflow conditions.  Use qemu_strto*() instead.

In the case of --partition, since we insist on a user-supplied
partition to be non-zero, we can use 0 rather than -1 for our
initial value to distinguish when a partition is not being
served, for slightly more optimal code.

The error messages for out-of-bounds values are less specific,
but should not be a terrible loss in quality.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v2: Retitle, catch more uses of strtol
[Hmm - this depends on int64_t and off_t being compatible; if they
aren't that way on all platforms, I'll need a temporary variable]
---
 qemu-nbd.c | 33 ++++++++++-----------------------
 1 file changed, 10 insertions(+), 23 deletions(-)

Comments

Richard W.M. Jones Dec. 15, 2018, 2:17 p.m. UTC | #1
On Sat, Dec 15, 2018 at 07:53:09AM -0600, Eric Blake wrote:
> Our copy-and-pasted open-coding of strtol handling forgot to
> handle overflow conditions.  Use qemu_strto*() instead.
> 
> In the case of --partition, since we insist on a user-supplied
> partition to be non-zero, we can use 0 rather than -1 for our
> initial value to distinguish when a partition is not being
> served, for slightly more optimal code.
> 
> The error messages for out-of-bounds values are less specific,
> but should not be a terrible loss in quality.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> ---
> v2: Retitle, catch more uses of strtol
> [Hmm - this depends on int64_t and off_t being compatible; if they
> aren't that way on all platforms, I'll need a temporary variable]
> ---
>  qemu-nbd.c | 33 ++++++++++-----------------------
>  1 file changed, 10 insertions(+), 23 deletions(-)
> 
> diff --git a/qemu-nbd.c b/qemu-nbd.c
> index 2807e132396..e3f739671b5 100644
> --- a/qemu-nbd.c
> +++ b/qemu-nbd.c
> @@ -544,9 +544,8 @@ int main(int argc, char **argv)
>      };
>      int ch;
>      int opt_ind = 0;
> -    char *end;
>      int flags = BDRV_O_RDWR;
> -    int partition = -1;
> +    int partition = 0;
>      int ret = 0;
>      bool seen_cache = false;
>      bool seen_discard = false;
> @@ -657,13 +656,9 @@ int main(int argc, char **argv)
>              port = optarg;
>              break;
>          case 'o':
> -                dev_offset = strtoll (optarg, &end, 0);
> -            if (*end) {
> -                error_report("Invalid offset `%s'", optarg);
> -                exit(EXIT_FAILURE);
> -            }
> -            if (dev_offset < 0) {
> -                error_report("Offset must be positive `%s'", optarg);
> +            if (qemu_strtoi64(optarg, NULL, 0, &dev_offset) < 0 ||
> +                dev_offset < 0) {
> +                error_report("Invalid offset '%s'", optarg);
>                  exit(EXIT_FAILURE);
>              }
>              break;
> @@ -685,13 +680,9 @@ int main(int argc, char **argv)
>              flags &= ~BDRV_O_RDWR;
>              break;
>          case 'P':
> -            partition = strtol(optarg, &end, 0);
> -            if (*end) {
> -                error_report("Invalid partition `%s'", optarg);
> -                exit(EXIT_FAILURE);
> -            }
> -            if (partition < 1 || partition > 8) {
> -                error_report("Invalid partition %d", partition);
> +            if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 ||
> +                partition < 1 || partition > 8) {
> +                error_report("Invalid partition '%s'", optarg);
>                  exit(EXIT_FAILURE);
>              }
>              break;
> @@ -709,15 +700,11 @@ int main(int argc, char **argv)
>              device = optarg;
>              break;
>          case 'e':
> -            shared = strtol(optarg, &end, 0);
> -            if (*end) {
> +            if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 ||
> +                shared < 1) {
>                  error_report("Invalid shared device number '%s'", optarg);
>                  exit(EXIT_FAILURE);
>              }
> -            if (shared < 1) {
> -                error_report("Shared device number must be greater than 0");
> -                exit(EXIT_FAILURE);
> -            }
>              break;
>          case 'f':
>              fmt = optarg;
> @@ -1006,7 +993,7 @@ int main(int argc, char **argv)
>      }
>      fd_size -= dev_offset;
> 
> -    if (partition != -1) {
> +    if (partition) {
>          ret = find_partition(blk, partition, &dev_offset, &fd_size);
>          if (ret < 0) {
>              error_report("Could not find partition %d: %s", partition,

Using qemu_strtoi has made the patch slightly bigger that last time
but is still a simplification, so:

Reviewed-by: Richard W.M. Jones <rjones@redhat.com>

Rich.
Vladimir Sementsov-Ogievskiy Dec. 18, 2018, 3:11 p.m. UTC | #2
15.12.2018 16:53, Eric Blake wrote:
> Our copy-and-pasted open-coding of strtol handling forgot to
> handle overflow conditions.  Use qemu_strto*() instead.
> 
> In the case of --partition, since we insist on a user-supplied
> partition to be non-zero, we can use 0 rather than -1 for our
> initial value to distinguish when a partition is not being
> served, for slightly more optimal code.
> 
> The error messages for out-of-bounds values are less specific,
> but should not be a terrible loss in quality.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>
> 
> ---
> v2: Retitle, catch more uses of strtol
> [Hmm - this depends on int64_t and off_t being compatible; if they
> aren't that way on all platforms, I'll need a temporary variable]

hmm, as I understand, even if this compatibility exists, it's not a part
of standard and nothing about off_t size in POSIX..

Moreover: what is the reason for using off_t in NBD code? We don't have it
in NBD protocol, we don't have it in generic block layer interface. Isn't it
always casted to int64_t or like this?
Eric Blake Jan. 4, 2019, 11:50 p.m. UTC | #3
On 12/18/18 9:11 AM, Vladimir Sementsov-Ogievskiy wrote:
> 15.12.2018 16:53, Eric Blake wrote:
>> Our copy-and-pasted open-coding of strtol handling forgot to
>> handle overflow conditions.  Use qemu_strto*() instead.
>>
>> In the case of --partition, since we insist on a user-supplied
>> partition to be non-zero, we can use 0 rather than -1 for our
>> initial value to distinguish when a partition is not being
>> served, for slightly more optimal code.
>>
>> The error messages for out-of-bounds values are less specific,
>> but should not be a terrible loss in quality.
>>
>> Signed-off-by: Eric Blake <eblake@redhat.com>
>>
>> ---
>> v2: Retitle, catch more uses of strtol
>> [Hmm - this depends on int64_t and off_t being compatible; if they
>> aren't that way on all platforms, I'll need a temporary variable]
> 
> hmm, as I understand, even if this compatibility exists, it's not a part
> of standard and nothing about off_t size in POSIX..

off_t allows you to run on older systems with 32-bit offsets and newer
systems with 64-bit offsets; but these days, even on 32-bit systems, we
compile qemu to always ask for 64-bit off_t.  Using off_t instead of
int64_t is probably a separate cleanup, but one that may be worth making
prior to this patch, so I'll defer this one to my v3.

> 
> Moreover: what is the reason for using off_t in NBD code? We don't have it
> in NBD protocol, we don't have it in generic block layer interface. Isn't it
> always casted to int64_t or like this?
>
Eric Blake Jan. 11, 2019, 10:47 p.m. UTC | #4
On 1/4/19 5:50 PM, Eric Blake wrote:

>> hmm, as I understand, even if this compatibility exists, it's not a part
>> of standard and nothing about off_t size in POSIX..
> 
> off_t allows you to run on older systems with 32-bit offsets and newer
> systems with 64-bit offsets; but these days, even on 32-bit systems, we
> compile qemu to always ask for 64-bit off_t.  Using off_t instead of
> int64_t is probably a separate cleanup, but one that may be worth making
> prior to this patch, so I'll defer this one to my v3.
> 
>>
>> Moreover: what is the reason for using off_t in NBD code? We don't have it
>> in NBD protocol, we don't have it in generic block layer interface. Isn't it
>> always casted to int64_t or like this?

Thanks again for asking this question. In auditing the use of off_t, I
found that 'qemu-nbd -P 1' happily tries to read beyond the bounds of
the BDS.  Thankfully, I can't find an exploit (escaped the CVE bullet -
no DoS assertion, overlarge malloc, information leak, or other nasty
problem), merely a permanent EIO down the road once the client tries to
access advertised available bytes; but I'm also adding a patch to my v3
series that does sanity checking (as we should NEVER blindly trust
values in a potentially-malicious image as being in bounds, so that
clients can't even connect to such images in the first place). [I also
think that qemu-nbd -P is seldom-used...]
diff mbox series

Patch

diff --git a/qemu-nbd.c b/qemu-nbd.c
index 2807e132396..e3f739671b5 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -544,9 +544,8 @@  int main(int argc, char **argv)
     };
     int ch;
     int opt_ind = 0;
-    char *end;
     int flags = BDRV_O_RDWR;
-    int partition = -1;
+    int partition = 0;
     int ret = 0;
     bool seen_cache = false;
     bool seen_discard = false;
@@ -657,13 +656,9 @@  int main(int argc, char **argv)
             port = optarg;
             break;
         case 'o':
-                dev_offset = strtoll (optarg, &end, 0);
-            if (*end) {
-                error_report("Invalid offset `%s'", optarg);
-                exit(EXIT_FAILURE);
-            }
-            if (dev_offset < 0) {
-                error_report("Offset must be positive `%s'", optarg);
+            if (qemu_strtoi64(optarg, NULL, 0, &dev_offset) < 0 ||
+                dev_offset < 0) {
+                error_report("Invalid offset '%s'", optarg);
                 exit(EXIT_FAILURE);
             }
             break;
@@ -685,13 +680,9 @@  int main(int argc, char **argv)
             flags &= ~BDRV_O_RDWR;
             break;
         case 'P':
-            partition = strtol(optarg, &end, 0);
-            if (*end) {
-                error_report("Invalid partition `%s'", optarg);
-                exit(EXIT_FAILURE);
-            }
-            if (partition < 1 || partition > 8) {
-                error_report("Invalid partition %d", partition);
+            if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 ||
+                partition < 1 || partition > 8) {
+                error_report("Invalid partition '%s'", optarg);
                 exit(EXIT_FAILURE);
             }
             break;
@@ -709,15 +700,11 @@  int main(int argc, char **argv)
             device = optarg;
             break;
         case 'e':
-            shared = strtol(optarg, &end, 0);
-            if (*end) {
+            if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 ||
+                shared < 1) {
                 error_report("Invalid shared device number '%s'", optarg);
                 exit(EXIT_FAILURE);
             }
-            if (shared < 1) {
-                error_report("Shared device number must be greater than 0");
-                exit(EXIT_FAILURE);
-            }
             break;
         case 'f':
             fmt = optarg;
@@ -1006,7 +993,7 @@  int main(int argc, char **argv)
     }
     fd_size -= dev_offset;

-    if (partition != -1) {
+    if (partition) {
         ret = find_partition(blk, partition, &dev_offset, &fd_size);
         if (ret < 0) {
             error_report("Could not find partition %d: %s", partition,