s390/qeth: overhaul L3 IP address dump code
authorJulian Wiedmann <jwi@linux.ibm.com>
Wed, 18 Dec 2019 16:34:44 +0000 (17:34 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 18 Dec 2019 20:34:56 +0000 (12:34 -0800)
The current code that dumps the RXIP/VIPA/IPATO addresses via sysfs
first checks whether the buffer still provides sufficient space to hold
another formatted address.
But the maximum length of an formatted IPv4 address is 15 characters,
not 12. So we underestimate the max required length and if the buffer
was previously filled to _just_ the right level, a formatted address can
end up being truncated.

Revamp these code paths to use the _actually_ required length of the
formatted IP address, and while at it suppress a gratuitous newline.

Also use scnprintf() to format the output. In case of a truncation, this
would allow us to return the number of characters that were actually
written.

Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/s390/net/qeth_l3.h
drivers/s390/net/qeth_l3_main.c
drivers/s390/net/qeth_l3_sys.c

index 5db04fe472c01bdbcc3911c3eb6db254e9d97a1c..2383ffad0a4a770de655ee654b89c9c913daa104 100644 (file)
@@ -102,7 +102,8 @@ struct qeth_ipato_entry {
 
 extern const struct attribute_group *qeth_l3_attr_groups[];
 
-void qeth_l3_ipaddr_to_string(enum qeth_prot_versions, const __u8 *, char *);
+int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr,
+                            char *buf);
 int qeth_l3_create_device_attributes(struct device *);
 void qeth_l3_remove_device_attributes(struct device *);
 int qeth_l3_setrouting_v4(struct qeth_card *);
index 27126330a4b003bd4e83e01e6ac654d191df04b0..3710761831ccd9388303014a715d673c1d232051 100644 (file)
@@ -44,23 +44,13 @@ static int qeth_l3_register_addr_entry(struct qeth_card *,
 static int qeth_l3_deregister_addr_entry(struct qeth_card *,
                struct qeth_ipaddr *);
 
-static void qeth_l3_ipaddr4_to_string(const __u8 *addr, char *buf)
-{
-       sprintf(buf, "%pI4", addr);
-}
-
-static void qeth_l3_ipaddr6_to_string(const __u8 *addr, char *buf)
-{
-       sprintf(buf, "%pI6", addr);
-}
-
-void qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const __u8 *addr,
-                               char *buf)
+int qeth_l3_ipaddr_to_string(enum qeth_prot_versions proto, const u8 *addr,
+                            char *buf)
 {
        if (proto == QETH_PROT_IPV4)
-               qeth_l3_ipaddr4_to_string(addr, buf);
-       else if (proto == QETH_PROT_IPV6)
-               qeth_l3_ipaddr6_to_string(addr, buf);
+               return sprintf(buf, "%pI4", addr);
+       else
+               return sprintf(buf, "%pI6", addr);
 }
 
 static struct qeth_ipaddr *qeth_l3_find_addr_by_ip(struct qeth_card *card,
index f9067ed6c7d32168997b33d529128a1f953ff7b5..aa9f3fc4544762680b02f445773d36ecf922b5d2 100644 (file)
@@ -386,30 +386,35 @@ static ssize_t qeth_l3_dev_ipato_add_show(char *buf, struct qeth_card *card,
                        enum qeth_prot_versions proto)
 {
        struct qeth_ipato_entry *ipatoe;
-       char addr_str[40];
-       int entry_len; /* length of 1 entry string, differs between v4 and v6 */
-       int i = 0;
+       int str_len = 0;
 
-       entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
-       /* add strlen for "/<mask>\n" */
-       entry_len += (proto == QETH_PROT_IPV4)? 5 : 6;
        mutex_lock(&card->ip_lock);
        list_for_each_entry(ipatoe, &card->ipato.entries, entry) {
+               char addr_str[40];
+               int entry_len;
+
                if (ipatoe->proto != proto)
                        continue;
-               /* String must not be longer than PAGE_SIZE. So we check if
-                * string length gets near PAGE_SIZE. Then we can savely display
-                * the next IPv6 address (worst case, compared to IPv4) */
-               if ((PAGE_SIZE - i) <= entry_len)
+
+               entry_len = qeth_l3_ipaddr_to_string(proto, ipatoe->addr,
+                                                    addr_str);
+               if (entry_len < 0)
+                       continue;
+
+               /* Append /%mask to the entry: */
+               entry_len += 1 + ((proto == QETH_PROT_IPV4) ? 2 : 3);
+               /* Enough room to format %entry\n into null terminated page? */
+               if (entry_len + 1 > PAGE_SIZE - str_len - 1)
                        break;
-               qeth_l3_ipaddr_to_string(proto, ipatoe->addr, addr_str);
-               i += snprintf(buf + i, PAGE_SIZE - i,
-                             "%s/%i\n", addr_str, ipatoe->mask_bits);
+
+               entry_len = scnprintf(buf, PAGE_SIZE - str_len,
+                                     "%s/%i\n", addr_str, ipatoe->mask_bits);
+               str_len += entry_len;
+               buf += entry_len;
        }
        mutex_unlock(&card->ip_lock);
-       i += snprintf(buf + i, PAGE_SIZE - i, "\n");
 
-       return i;
+       return str_len ? str_len : scnprintf(buf, PAGE_SIZE, "\n");
 }
 
 static ssize_t qeth_l3_dev_ipato_add4_show(struct device *dev,
@@ -607,31 +612,34 @@ static ssize_t qeth_l3_dev_ip_add_show(struct device *dev, char *buf,
 {
        struct qeth_card *card = dev_get_drvdata(dev);
        struct qeth_ipaddr *ipaddr;
-       char addr_str[40];
        int str_len = 0;
-       int entry_len; /* length of 1 entry string, differs between v4 and v6 */
        int i;
 
-       entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
-       entry_len += 2; /* \n + terminator */
        mutex_lock(&card->ip_lock);
        hash_for_each(card->ip_htable, i, ipaddr, hnode) {
+               char addr_str[40];
+               int entry_len;
+
                if (ipaddr->proto != proto || ipaddr->type != type)
                        continue;
-               /* String must not be longer than PAGE_SIZE. So we check if
-                * string length gets near PAGE_SIZE. Then we can savely display
-                * the next IPv6 address (worst case, compared to IPv4) */
-               if ((PAGE_SIZE - str_len) <= entry_len)
+
+               entry_len = qeth_l3_ipaddr_to_string(proto, (u8 *)&ipaddr->u,
+                                                    addr_str);
+               if (entry_len < 0)
+                       continue;
+
+               /* Enough room to format %addr\n into null terminated page? */
+               if (entry_len + 1 > PAGE_SIZE - str_len - 1)
                        break;
-               qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u,
-                       addr_str);
-               str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n",
-                                   addr_str);
+
+               entry_len = scnprintf(buf, PAGE_SIZE - str_len, "%s\n",
+                                     addr_str);
+               str_len += entry_len;
+               buf += entry_len;
        }
        mutex_unlock(&card->ip_lock);
-       str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n");
 
-       return str_len;
+       return str_len ? str_len : scnprintf(buf, PAGE_SIZE, "\n");
 }
 
 static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev,