From: a.ryabinin@samsung.com (Andrey Ryabinin)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] arm: put_user: fix possible data corruption in put_user
Date: Mon, 05 May 2014 14:50:58 +0400 [thread overview]
Message-ID: <53676D12.9040300@samsung.com> (raw)
In-Reply-To: <20140505090126.GD3693@n2100.arm.linux.org.uk>
On 05/05/14 13:01, Russell King - ARM Linux wrote:
> On Mon, May 05, 2014 at 10:13:58AM +0400, Andrey Ryabinin wrote:
>> According to arm procedure call standart r2 register is call-cloberred.
>> So after the result of x expression was put into r2 any following
>> function call in p may overwrite r2. To fix this, the result of p
>> expression must be saved to the temporary variable before the
>> assigment x expression to __r2.
>
> This and the patch make no sense. You talk about r2, but you're doing
> nothing with r2 in the patch.
>
No, you didn't get it. I'll try to explain better.
Lets consider following example:
unsigned int __user *get_address(void);
...
put_user(1, get_address());
...
Pay attention that in get_address function register r2 may be used.
In above example, without my patch, put_user macro will be expanded to the following code:
...
register const unsigned int __r2 asm("r2") = (1);
register const unsigned int __user *__p asm("r0") = (get_address());
...
At first we put value 1 into r2 register. After that get_address is called, and clobbers r2 register.
This means that after assignment to variable __p, register r2 may no longer contain a valid value - 1.
My patch put get_address calls befor the assignment of (x) to __r2.
With my patch, put_user macro will be expanded to the following code:
...
const unsigned int __user *tmp_p = (get_address());
register const unsigned int __r2 asm("r2") = (1);
register const unsigned int __user *__p asm("r0") = tmp_p;
...
In this time get_address() call happens before loading 1 to r2, so it won't be corrupted.
Here is the full code of test, so anyone could check.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/uaccess.h>
unsigned int x = 0;
unsigned int y = 0;
/* get_address returns address of x, and clobbers r2 register */
unsigned int __user *get_address(void)
{
mm_segment_t oldfs;
oldfs = get_fs();
set_fs(get_ds());
put_user(2, &y); /* this put_user call will put value 2 in register r2 */
set_fs(oldfs);
return &x;
}
static __init int test_init(void)
{
mm_segment_t oldfs;
oldfs = get_fs();
set_fs(get_ds());
put_user(1, get_address()); /* put 1 to x */
set_fs(oldfs);
printk("\nput_user_test: value %x\n\n", *get_address()); /* this will print "put_user_test: value 2" instead of "put_user_test: value 1"
return 0;
}
module_init(test_init);
WARNING: multiple messages have this Message-ID (diff)
From: Andrey Ryabinin <a.ryabinin@samsung.com>
To: Russell King - ARM Linux <linux@arm.linux.org.uk>
Cc: nicolas.pitre@linaro.org, will.deacon@arm.com,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH] arm: put_user: fix possible data corruption in put_user
Date: Mon, 05 May 2014 14:50:58 +0400 [thread overview]
Message-ID: <53676D12.9040300@samsung.com> (raw)
In-Reply-To: <20140505090126.GD3693@n2100.arm.linux.org.uk>
On 05/05/14 13:01, Russell King - ARM Linux wrote:
> On Mon, May 05, 2014 at 10:13:58AM +0400, Andrey Ryabinin wrote:
>> According to arm procedure call standart r2 register is call-cloberred.
>> So after the result of x expression was put into r2 any following
>> function call in p may overwrite r2. To fix this, the result of p
>> expression must be saved to the temporary variable before the
>> assigment x expression to __r2.
>
> This and the patch make no sense. You talk about r2, but you're doing
> nothing with r2 in the patch.
>
No, you didn't get it. I'll try to explain better.
Lets consider following example:
unsigned int __user *get_address(void);
...
put_user(1, get_address());
...
Pay attention that in get_address function register r2 may be used.
In above example, without my patch, put_user macro will be expanded to the following code:
...
register const unsigned int __r2 asm("r2") = (1);
register const unsigned int __user *__p asm("r0") = (get_address());
...
At first we put value 1 into r2 register. After that get_address is called, and clobbers r2 register.
This means that after assignment to variable __p, register r2 may no longer contain a valid value - 1.
My patch put get_address calls befor the assignment of (x) to __r2.
With my patch, put_user macro will be expanded to the following code:
...
const unsigned int __user *tmp_p = (get_address());
register const unsigned int __r2 asm("r2") = (1);
register const unsigned int __user *__p asm("r0") = tmp_p;
...
In this time get_address() call happens before loading 1 to r2, so it won't be corrupted.
Here is the full code of test, so anyone could check.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/uaccess.h>
unsigned int x = 0;
unsigned int y = 0;
/* get_address returns address of x, and clobbers r2 register */
unsigned int __user *get_address(void)
{
mm_segment_t oldfs;
oldfs = get_fs();
set_fs(get_ds());
put_user(2, &y); /* this put_user call will put value 2 in register r2 */
set_fs(oldfs);
return &x;
}
static __init int test_init(void)
{
mm_segment_t oldfs;
oldfs = get_fs();
set_fs(get_ds());
put_user(1, get_address()); /* put 1 to x */
set_fs(oldfs);
printk("\nput_user_test: value %x\n\n", *get_address()); /* this will print "put_user_test: value 2" instead of "put_user_test: value 1"
return 0;
}
module_init(test_init);
next prev parent reply other threads:[~2014-05-05 10:50 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-05-05 6:13 [PATCH] arm: put_user: fix possible data corruption in put_user Andrey Ryabinin
2014-05-05 6:13 ` Andrey Ryabinin
2014-05-05 9:01 ` Russell King - ARM Linux
2014-05-05 9:01 ` Russell King - ARM Linux
2014-05-05 10:50 ` Andrey Ryabinin [this message]
2014-05-05 10:50 ` Andrey Ryabinin
2014-05-05 13:45 ` Nicolas Pitre
2014-05-05 13:45 ` Nicolas Pitre
2014-05-05 14:15 ` Nicolas Pitre
2014-05-05 14:15 ` Nicolas Pitre
2014-05-06 7:11 ` [PATCHv2] " Andrey Ryabinin
2014-05-06 7:11 ` Andrey Ryabinin
2014-05-06 20:13 ` Nicolas Pitre
2014-05-06 20:13 ` Nicolas Pitre
2014-05-07 13:19 ` Andrey Ryabinin
2014-05-07 13:19 ` Andrey Ryabinin
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=53676D12.9040300@samsung.com \
--to=a.ryabinin@samsung.com \
--cc=linux-arm-kernel@lists.infradead.org \
/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.