From 51f299dd94bb1e28d03eefbc4fe0b9282f9ee2fa Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 Nov 2017 00:15:04 +0100 Subject: [PATCH 1/7] net: core: improve sanity checking in __dev_alloc_name __dev_alloc_name is called from the public (and exported) dev_alloc_name(), so we don't have a guarantee that strlen(name) is at most IFNAMSIZ. If somebody manages to get __dev_alloc_name called with a % char beyond the 31st character, we'd be making a snprintf() call that will very easily crash the kernel (using an appropriate %p extension, we'll likely dereference some completely bogus pointer). In the normal case where strlen() is sane, we don't even save anything by limiting to IFNAMSIZ, so just use strchr(). Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index 658337bf33e4..1a5d31fdea27 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1064,7 +1064,7 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) unsigned long *inuse; struct net_device *d; - p = strnchr(name, IFNAMSIZ-1, '%'); + p = strchr(name, '%'); if (p) { /* * Verify the string as this thing may have come from From 2c88b855981481970b731bf3f4508400aac429fb Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 Nov 2017 00:15:05 +0100 Subject: [PATCH 2/7] net: core: move dev_alloc_name_ns a little higher No functional change. Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- net/core/dev.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 1a5d31fdea27..4545685d2fa7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1107,6 +1107,19 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) return -ENFILE; } +static int dev_alloc_name_ns(struct net *net, + struct net_device *dev, + const char *name) +{ + char buf[IFNAMSIZ]; + int ret; + + ret = __dev_alloc_name(net, name, buf); + if (ret >= 0) + strlcpy(dev->name, buf, IFNAMSIZ); + return ret; +} + /** * dev_alloc_name - allocate a name for a device * @dev: device @@ -1136,19 +1149,6 @@ int dev_alloc_name(struct net_device *dev, const char *name) } EXPORT_SYMBOL(dev_alloc_name); -static int dev_alloc_name_ns(struct net *net, - struct net_device *dev, - const char *name) -{ - char buf[IFNAMSIZ]; - int ret; - - ret = __dev_alloc_name(net, name, buf); - if (ret >= 0) - strlcpy(dev->name, buf, IFNAMSIZ); - return ret; -} - int dev_get_valid_name(struct net *net, struct net_device *dev, const char *name) { From c46d7642e915106b0301bc4d53a79e8e806c2eb9 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 Nov 2017 00:15:06 +0100 Subject: [PATCH 3/7] net: core: eliminate dev_alloc_name{,_ns} code duplication dev_alloc_name contained a BUG_ON(), which I moved to dev_alloc_name_ns; the only other caller of that already has the same BUG_ON. Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- net/core/dev.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 4545685d2fa7..7580c2046c95 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1114,6 +1114,7 @@ static int dev_alloc_name_ns(struct net *net, char buf[IFNAMSIZ]; int ret; + BUG_ON(!net); ret = __dev_alloc_name(net, name, buf); if (ret >= 0) strlcpy(dev->name, buf, IFNAMSIZ); @@ -1136,16 +1137,7 @@ static int dev_alloc_name_ns(struct net *net, int dev_alloc_name(struct net_device *dev, const char *name) { - char buf[IFNAMSIZ]; - struct net *net; - int ret; - - BUG_ON(!dev_net(dev)); - net = dev_net(dev); - ret = __dev_alloc_name(net, name, buf); - if (ret >= 0) - strlcpy(dev->name, buf, IFNAMSIZ); - return ret; + return dev_alloc_name_ns(dev_net(dev), dev, name); } EXPORT_SYMBOL(dev_alloc_name); From 6224abda0db8845756571833744d4414f144ecb5 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 Nov 2017 00:15:07 +0100 Subject: [PATCH 4/7] net: core: drop pointless check in __dev_alloc_name The only caller passes a stack buffer as buf, so it won't equal the passed-in name. Moreover, we're already using buf as a scratch buffer inside the if (p) {} block, so if buf and name were the same, that snprintf() call would be overwriting its own format string. Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- net/core/dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 7580c2046c95..4cedc7595f1f 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1095,8 +1095,7 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) free_page((unsigned long) inuse); } - if (buf != name) - snprintf(buf, IFNAMSIZ, name, i); + snprintf(buf, IFNAMSIZ, name, i); if (!__dev_get_by_name(net, buf)) return i; From 93809105cf9d43790839d8b8e29a8a505290ec68 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 Nov 2017 00:15:08 +0100 Subject: [PATCH 5/7] net: core: check dev_valid_name in __dev_alloc_name We currently only exclude non-sysfs-friendly names via dev_get_valid_name; there doesn't seem to be a reason to allow such names when we're called via dev_alloc_name. This does duplicate the dev_valid_name check in the dev_get_valid_name() case; we'll fix that shortly. Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- net/core/dev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index 4cedc7595f1f..cb3d95edf58d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1064,6 +1064,9 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) unsigned long *inuse; struct net_device *d; + if (!dev_valid_name(name)) + return -EINVAL; + p = strchr(name, '%'); if (p) { /* From d6f295e9def0bee85b37bdffb95153721935c342 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 Nov 2017 00:15:09 +0100 Subject: [PATCH 6/7] net: core: maybe return -EEXIST in __dev_alloc_name If we're given format string with no %d, -EEXIST is a saner error code. Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index cb3d95edf58d..1bb856eaed1c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1106,7 +1106,7 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) * when the name is long and there isn't enough space left * for the digits, or if all bits are used. */ - return -ENFILE; + return p ? -ENFILE : -EEXIST; } static int dev_alloc_name_ns(struct net *net, From 87c320e51519a83c496ab7bfb4e96c8f9c001e89 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Mon, 13 Nov 2017 00:15:10 +0100 Subject: [PATCH 7/7] net: core: dev_get_valid_name is now the same as dev_alloc_name_ns If name contains a %, it's easy to see that this patch doesn't change anything (other than eliminate the duplicate dev_valid_name call). Otherwise, we'll now just spend a little time in snprintf() copying name to the stack buffer allocated in dev_alloc_name_ns, and do the __dev_get_by_name using that buffer rather than name. Signed-off-by: Rasmus Villemoes Signed-off-by: David S. Miller --- net/core/dev.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 1bb856eaed1c..ad5f90dacd92 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1146,19 +1146,7 @@ EXPORT_SYMBOL(dev_alloc_name); int dev_get_valid_name(struct net *net, struct net_device *dev, const char *name) { - BUG_ON(!net); - - if (!dev_valid_name(name)) - return -EINVAL; - - if (strchr(name, '%')) - return dev_alloc_name_ns(net, dev, name); - else if (__dev_get_by_name(net, name)) - return -EEXIST; - else if (dev->name != name) - strlcpy(dev->name, name, IFNAMSIZ); - - return 0; + return dev_alloc_name_ns(net, dev, name); } EXPORT_SYMBOL(dev_get_valid_name);