I was rewrited the patch for hostctl command and sys/dev/pvbus.

From: "Theo de Raadt" <dera...@openbsd.org>
Date: Tue, 11 Oct 2022 19:09:42 -0600

> An example of this mechanism is SIOCGIFCONF.  The ioctl passes a pointer
> to a struct containing length & pointer to data.  See net/if.c ifconf()
> There are other similar schemes, but they all come down to asking the
> kernel for the size and then doing a 2nd ioctl.
> 
> Or a 3rd or more calls, in case the value has changed in the meantime and
> grown even further, but userland can realloc() the storage until it wins.
> 

My new patch is not returned value length from ioctl() system call
when read the KEY's value.

The hostctl command allocate 4k bytes memory for store the value. Then
read the value by ioctl() system call. If ioctl() returned -1 end
errno is ERANGE, then hostctl comannd reallocate twice as much space.
The upper limit is PVBUS_KVOP_MAXSIZE (64k bytes).
 
Check the patch, please.

comment, ok?
--
ASOU Masato

diff --git a/share/man/man4/pvbus.4 b/share/man/man4/pvbus.4
index 8d67809e9c0..3c6681decf0 100644
--- a/share/man/man4/pvbus.4
+++ b/share/man/man4/pvbus.4
@@ -125,6 +125,13 @@ Read the value from
 .Fa pvr_key
 and return it in
 .Fa pvr_value .
+If
+.Fa pvr_valuelen
+is not enough for the value,
+the command will fail and
+.Xr errno 2
+is set to
+.Er ERANGE .
 .It Dv PVBUSIOC_KVTYPE
 Return the type of the attached hypervisor interface as a string in
 .Fa pvr_key ;
diff --git a/sys/dev/pv/hypervic.c b/sys/dev/pv/hypervic.c
index 9c7f70dd96f..a7455d03e5b 100644
--- a/sys/dev/pv/hypervic.c
+++ b/sys/dev/pv/hypervic.c
@@ -1151,11 +1151,12 @@ hv_kvop(void *arg, int op, char *key, char *val, size_t 
vallen)
        kvpl = &kvp->kvp_pool[pool];
        if (strlen(key) == 0) {
                for (next = 0; next < MAXPOOLENTS; next++) {
-                       if ((val + vallen < vp + HV_KVP_MAX_KEY_SIZE / 2) ||
-                           kvp_pool_keys(kvpl, next, vp, &keylen))
+                       if (val + vallen < vp + HV_KVP_MAX_KEY_SIZE / 2)
+                               return (ERANGE);
+                       if (kvp_pool_keys(kvpl, next, vp, &keylen))
                                goto out;
                        if (strlcat(val, "\n", vallen) >= vallen)
-                               goto out;
+                               return (ERANGE);
                        vp += keylen;
                }
  out:
diff --git a/sys/dev/pv/pvbus.c b/sys/dev/pv/pvbus.c
index 5f7c4b57fe0..c76a9e81444 100644
--- a/sys/dev/pv/pvbus.c
+++ b/sys/dev/pv/pvbus.c
@@ -400,12 +400,12 @@ pvbusgetstr(size_t srclen, const char *src, char **dstp)
        /*
         * Reject size that is too short or obviously too long:
         * - at least one byte for the nul terminator.
-        * - PAGE_SIZE is an arbitrary value, but known pv backends seem
-        *   to have a hard (PAGE_SIZE - x) limit in their messaging.
+        * - PVBUS_KVOP_MAXSIZE is an arbitrary value, but known pv backends
+        *   seem to have a hard (PAGE_SIZE - x) limit in their messaging.
         */
        if (srclen < 1)
                return (EINVAL);
-       else if (srclen > PAGE_SIZE)
+       else if (srclen > PVBUS_KVOP_MAXSIZE)
                return (ENAMETOOLONG);
 
        *dstp = dst = malloc(srclen + 1, M_TEMP, M_WAITOK | M_ZERO);
diff --git a/sys/dev/pv/pvvar.h b/sys/dev/pv/pvvar.h
index 4e23ae52bd5..333d4fddf9c 100644
--- a/sys/dev/pv/pvvar.h
+++ b/sys/dev/pv/pvvar.h
@@ -30,6 +30,8 @@ struct pvbus_req {
 #define PVBUSIOC_KVWRITE       _IOWR('V', 2, struct pvbus_req)
 #define PVBUSIOC_TYPE          _IOWR('V', 3, struct pvbus_req)
 
+#define        PVBUS_KVOP_MAXSIZE      (64 * 1024)
+
 #ifdef _KERNEL
 enum {
        PVBUS_KVM,
diff --git a/sys/dev/pv/xenstore.c b/sys/dev/pv/xenstore.c
index 494eb40bfb0..c32f1cea7d7 100644
--- a/sys/dev/pv/xenstore.c
+++ b/sys/dev/pv/xenstore.c
@@ -1072,6 +1072,7 @@ xs_kvop(void *xsc, int op, char *key, char *value, size_t 
valuelen)
        struct xs_transaction xst;
        struct iovec iov, *iovp = &iov;
        int error = 0, iov_cnt = 0, cmd, i;
+       size_t pos;
 
        switch (op) {
        case PVBUS_KVWRITE:
@@ -1115,12 +1116,19 @@ xs_kvop(void *xsc, int op, char *key, char *value, 
size_t valuelen)
                }
                /* FALLTHROUGH */
        case XS_LIST:
-               for (i = 0; i < iov_cnt; i++) {
-                       if (i && strlcat(value, "\n", valuelen) >= valuelen)
-                               break;
-                       if (strlcat(value, iovp[i].iov_base,
-                           valuelen) >= valuelen)
+               for (i = pos = 0; i < iov_cnt; i++) {
+                       if (i) {
+                               if (pos + 1 >= valuelen) {
+                                       error = ERANGE;
+                                       break;
+                               }
+                               value[pos++] = '\n';
+                       }
+                       if (strlen(iovp[i].iov_base) >= valuelen) {
+                               error = ERANGE;
                                break;
+                       }
+                       pos += strlcat(&value[pos], iovp[i].iov_base, valuelen 
- pos);
                }
                xs_resfree(&xst, iovp, iov_cnt);
                break;
@@ -1128,5 +1136,5 @@ xs_kvop(void *xsc, int op, char *key, char *value, size_t 
valuelen)
                break;
        }
 
-       return (0);
+       return (error);
 }
diff --git a/usr.sbin/hostctl/hostctl.c b/usr.sbin/hostctl/hostctl.c
index f0639dd55f3..ff29169005a 100644
--- a/usr.sbin/hostctl/hostctl.c
+++ b/usr.sbin/hostctl/hostctl.c
@@ -178,15 +178,29 @@ main(int argc, char *argv[])
                usage();
 
        /* Re-open read-writable */
-       if (cmd == PVBUSIOC_KVWRITE) {
+       if (cmd != PVBUSIOC_KVREAD) {
                close(fd);
                if ((fd = open(path_pvbus, O_RDWR)) == -1)
                        err(1, "open: %s", path_pvbus);
+               if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == -1)
+                       err(1, "ioctl");
+       } else {
+               while (1) {
+                       if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == 0)
+                               break;
+                       if (errno == ERANGE &&
+                           pvr.pvr_valuelen < PVBUS_KVOP_MAXSIZE) {
+                               /* the buffer is not enough, expand it */
+                               pvr.pvr_valuelen *= 2;
+                               if ((pvr.pvr_value = realloc(pvr.pvr_value,
+                                   pvr.pvr_valuelen)) == NULL)
+                                       err(1, "realloc");
+                               continue;
+                       }
+                       err(1, "ioctl");
+               }
        }
 
-       if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == -1)
-               err(1, "ioctl");
-
        if (!qflag && strlen(pvr.pvr_value)) {
                /*
                 * The value can contain newlines and basically anything;

Reply via email to