All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arnd Bergmann <arnd@arndb.de>
To: Arjan van de Ven <arjan@infradead.org>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>,
	Ingo Molnar <mingo@elte.hu>, David Miller <davem@davemloft.net>,
	Andrew Morton <akpm@linux-foundation.org>,
	linux-kernel@vger.kernel.org
Subject: Re: strict copy_from_user checks issues?
Date: Tue, 5 Jan 2010 16:20:38 +0100	[thread overview]
Message-ID: <201001051620.38943.arnd@arndb.de> (raw)
In-Reply-To: <20100105055224.39f9efff@infradead.org>

On Tuesday 05 January 2010, Arjan van de Ven wrote:
> On Tue, 5 Jan 2010 14:45:25 +0100
> Arnd Bergmann <arnd@arndb.de> wrote:
> > 
> > I think it will get inlined on 32 bit machines or without
> > CONFIG_COMPAT, but not when CONFIG_COMPAT is enabled, because then
> > there are two call-sites.
> 
> one of them is buggy it seems;
> it passes in a shorter length, but there is no code in sight that makes
> sure that the end of the structure (the difference between the shorter
> and full length one) gets initialized to, say, zeros rather than stack
> garbage. So looks like there is at least a bug there.

The old code (until 2.6.32) always copied sizeof (struct ifreq), which
I changed in order not corrupt the user space stack.

I checked that no code in the tun driver accesses the incompatible
parts of the structure, which would require conversion rather
than simply doing a short read, so it looks correct to me, or am I
missing something?

> Would be nice if the copy (+ clear) would be pulled to the two callers
> I suspect... at which point the warning will go away too as a side
> effect.

You mean like this?

It adds some complexity and about 200 bytes of object code,
I'm not sure it's worth it.

--
[UNTESTED PATCH] tun: avoid copy_from_user size warning

For 32 bit compat code, the tun driver only copies the
fields it needs using a short length, which copy_from_user
now warns about. Moving the copy operation outside of the
main ioctl function lets us avoid the warning.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>

---

 drivers/net/tun.c |  104 +++++++++++++++++++++++++++++++----------------------
 1 files changed, 61 insertions(+), 43 deletions(-)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 01e99f2..8eb9f38 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1111,19 +1111,14 @@ static int set_offload(struct net_device *dev, unsigned long arg)
 }
 
 static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
-			    unsigned long arg, int ifreq_len)
+			    unsigned long arg, struct ifreq *ifr)
 {
 	struct tun_file *tfile = file->private_data;
 	struct tun_struct *tun;
 	void __user* argp = (void __user*)arg;
-	struct ifreq ifr;
 	int sndbuf;
 	int ret;
 
-	if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
-		if (copy_from_user(&ifr, argp, ifreq_len))
-			return -EFAULT;
-
 	if (cmd == TUNGETFEATURES) {
 		/* Currently this just means: "what IFF flags are valid?".
 		 * This is needed because we never checked for invalid flags on
@@ -1136,34 +1131,21 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
 	rtnl_lock();
 
 	tun = __tun_get(tfile);
-	if (cmd == TUNSETIFF && !tun) {
-		ifr.ifr_name[IFNAMSIZ-1] = '\0';
-
-		ret = tun_set_iff(tfile->net, file, &ifr);
-
-		if (ret)
-			goto unlock;
-
-		if (copy_to_user(argp, &ifr, ifreq_len))
-			ret = -EFAULT;
+	if (!tun) {
+		ret = -EBADFD;
+		if (cmd == TUNSETIFF) {
+			ifr->ifr_name[IFNAMSIZ-1] = '\0';
+			ret = tun_set_iff(tfile->net, file, ifr);
+		}
 		goto unlock;
 	}
 
-	ret = -EBADFD;
-	if (!tun)
-		goto unlock;
-
 	DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd);
 
 	ret = 0;
 	switch (cmd) {
 	case TUNGETIFF:
-		ret = tun_get_iff(current->nsproxy->net_ns, tun, &ifr);
-		if (ret)
-			break;
-
-		if (copy_to_user(argp, &ifr, ifreq_len))
-			ret = -EFAULT;
+		ret = tun_get_iff(current->nsproxy->net_ns, tun, ifr);
 		break;
 
 	case TUNSETNOCSUM:
@@ -1234,18 +1216,16 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
 
 	case SIOCGIFHWADDR:
 		/* Get hw addres */
-		memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN);
-		ifr.ifr_hwaddr.sa_family = tun->dev->type;
-		if (copy_to_user(argp, &ifr, ifreq_len))
-			ret = -EFAULT;
+		memcpy(ifr->ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN);
+		ifr->ifr_hwaddr.sa_family = tun->dev->type;
 		break;
 
 	case SIOCSIFHWADDR:
 		/* Set hw address */
 		DBG(KERN_DEBUG "%s: set hw address: %pM\n",
-			tun->dev->name, ifr.ifr_hwaddr.sa_data);
+			tun->dev->name, ifr->ifr_hwaddr.sa_data);
 
-		ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr);
+		ret = dev_set_mac_address(tun->dev, &ifr->ifr_hwaddr);
 		break;
 
 	case TUNGETSNDBUF:
@@ -1278,35 +1258,73 @@ unlock:
 static long tun_chr_ioctl(struct file *file,
 			  unsigned int cmd, unsigned long arg)
 {
-	return __tun_chr_ioctl(file, cmd, arg, sizeof (struct ifreq));
+	struct ifreq ifr;
+	struct ifreq __user *uifr = (void *)arg;
+	int ret;
+
+	switch (cmd) {
+	case TUNSETIFF:
+	case TUNGETIFF:
+	case SIOCGIFHWADDR:
+	case SIOCSIFHWADDR:
+		if (copy_from_user(&ifr, uifr, sizeof (ifr)))
+			return -EFAULT;
+
+		ret = __tun_chr_ioctl(file, cmd, arg, &ifr);
+		if (ret < 0)
+			return ret;
+
+		if (copy_to_user(uifr, &ifr, sizeof (ifr)))
+			return -EFAULT;
+
+		return ret;
+	}
+
+	return __tun_chr_ioctl(file, cmd, arg, NULL);
 }
 
 #ifdef CONFIG_COMPAT
 static long tun_chr_compat_ioctl(struct file *file,
 			 unsigned int cmd, unsigned long arg)
 {
+	struct ifreq ifr;
+	struct compat_ifreq __user *uifr = compat_ptr(arg);
+	int ret;
+
 	switch (cmd) {
 	case TUNSETIFF:
 	case TUNGETIFF:
+	case SIOCGIFHWADDR:
+	case SIOCSIFHWADDR:
+	/*
+	 * compat_ifreq is shorter than ifreq, so we must not access beyond
+	 * the end of that structure. All fields that are used in this
+	 * driver are compatible though, we don't need to convert the
+	 * contents.
+	 */
+		memset(&ifr, 0, sizeof (ifr));
+		if (copy_from_user((struct compat_ifreq *)&ifr, uifr, sizeof (*uifr)))
+			return -EFAULT;
+
+		ret = __tun_chr_ioctl(file, cmd, 0, &ifr);
+		if (ret)
+			return ret;
+
+		if (copy_to_user(uifr, (struct compat_ifreq *)&ifr, sizeof (*uifr)))
+			return -EFAULT;
+		return ret;
+		break;
+
 	case TUNSETTXFILTER:
 	case TUNGETSNDBUF:
 	case TUNSETSNDBUF:
-	case SIOCGIFHWADDR:
-	case SIOCSIFHWADDR:
 		arg = (unsigned long)compat_ptr(arg);
 		break;
 	default:
 		arg = (compat_ulong_t)arg;
 		break;
 	}
-
-	/*
-	 * compat_ifreq is shorter than ifreq, so we must not access beyond
-	 * the end of that structure. All fields that are used in this
-	 * driver are compatible though, we don't need to convert the
-	 * contents.
-	 */
-	return __tun_chr_ioctl(file, cmd, arg, sizeof(struct compat_ifreq));
+	return __tun_chr_ioctl(file, cmd, arg, NULL);
 }
 #endif /* CONFIG_COMPAT */
 


  reply	other threads:[~2010-01-05 15:21 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-01-04 15:43 strict copy_from_user checks issues? Heiko Carstens
2010-01-05  1:43 ` Arjan van de Ven
2010-01-05  7:35   ` Ingo Molnar
2010-01-05  9:48   ` Heiko Carstens
2010-01-05 12:47     ` Arnd Bergmann
2010-01-05 13:19       ` Heiko Carstens
2010-01-05 13:31         ` Arjan van de Ven
2010-01-05 15:22           ` [PATCH] sparc: copy_from_user() should not return -EFAULT Heiko Carstens
2010-01-05 17:27             ` Andi Kleen
2010-01-05 20:47               ` David Miller
2010-01-06  3:20               ` Arjan van de Ven
2010-01-05 17:55             ` Arnd Bergmann
2010-01-06  4:42             ` David Miller
2010-01-05 22:15         ` [tip:x86/urgent] x86: " tip-bot for Heiko Carstens
2010-01-05 13:34     ` strict copy_from_user checks issues? Arjan van de Ven
2010-01-05 13:36       ` Arjan van de Ven
2010-01-05 13:45       ` Arnd Bergmann
2010-01-05 13:52         ` Arjan van de Ven
2010-01-05 15:20           ` Arnd Bergmann [this message]
2010-01-05 21:44             ` H. Peter Anvin
2010-01-07 14:02               ` Arnd Bergmann
2010-01-07 23:57                 ` H. Peter Anvin
2010-01-09  0:07                   ` Arnd Bergmann
2010-01-09  0:10                     ` H. Peter Anvin
2010-01-09  8:01                       ` Arnd Bergmann
2010-01-09 20:57                         ` H. Peter Anvin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=201001051620.38943.arnd@arndb.de \
    --to=arnd@arndb.de \
    --cc=akpm@linux-foundation.org \
    --cc=arjan@infradead.org \
    --cc=davem@davemloft.net \
    --cc=heiko.carstens@de.ibm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@elte.hu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.