linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* __user with scalar data types
@ 2017-06-19 16:15 Jordan Crouse
  2017-06-19 16:34 ` Al Viro
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Jordan Crouse @ 2017-06-19 16:15 UTC (permalink / raw)
  To: linux-sparse; +Cc: linux-kernel, dri-devel

A number of us over in DRM land have been using __u64 scalar types
to store pointers for uapi structures in accordance with Daniel Vetter's
now classic treatise on ioctls:

http://blog.ffwll.ch/2013/11/botching-up-ioctls.html

A smaller number of us have further been marking the __u64 with __user,
to wit:

struct uapistruct {
	...
	__u64 __user myptr;
	---
};

And then converting it for use in the kernel as such:

{
	void __user *userptr = (void __user *)(uintptr_t)args->myptr;

	copy_from_user(local, userptr, size);
	...
}

The problem is that sparse doesn't like the momentary switch to
uintptr_t:

	warning: dereference of noderef expression

Which raised a bikeshed debate over whether it is appropriate to mark a scalar
type as __user.  My opinion is that it is appropriate because __user should mark
user memory regardless of the container.

I'm looking for opinions or semi-authoritative edicts to determine if we should
either start changing our uapi headers or go off and try to figure out how to
make sparse understand this particular usage.

Thanks!
Jordan
-- 
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: __user with scalar data types
  2017-06-19 16:15 __user with scalar data types Jordan Crouse
@ 2017-06-19 16:34 ` Al Viro
  2017-06-20  7:12   ` Daniel Vetter
  2017-06-19 19:27 ` Luc Van Oostenryck
  2017-06-19 20:32 ` Luc Van Oostenryck
  2 siblings, 1 reply; 9+ messages in thread
From: Al Viro @ 2017-06-19 16:34 UTC (permalink / raw)
  To: linux-sparse, linux-kernel, dri-devel

On Mon, Jun 19, 2017 at 10:15:09AM -0600, Jordan Crouse wrote:

> Which raised a bikeshed debate over whether it is appropriate to mark a scalar
> type as __user.  My opinion is that it is appropriate because __user should mark
> user memory regardless of the container.

What the hell?  __user is a qualifier like const, volatile, etc.  It's a property
of *pointer* *type*.  Not some nebulous "marks userland memory" thing.

> I'm looking for opinions or semi-authoritative edicts to determine if we should
> either start changing our uapi headers or go off and try to figure out how to
> make sparse understand this particular usage.

Stop cargo-culting, please.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: __user with scalar data types
  2017-06-19 16:15 __user with scalar data types Jordan Crouse
  2017-06-19 16:34 ` Al Viro
@ 2017-06-19 19:27 ` Luc Van Oostenryck
  2017-06-19 20:32 ` Luc Van Oostenryck
  2 siblings, 0 replies; 9+ messages in thread
From: Luc Van Oostenryck @ 2017-06-19 19:27 UTC (permalink / raw)
  To: Jordan Crouse; +Cc: linux-sparse, linux-kernel, dri-devel

On Mon, Jun 19, 2017 at 10:15:09AM -0600, Jordan Crouse wrote:
> A number of us over in DRM land have been using __u64 scalar types
> to store pointers for uapi structures in accordance with Daniel Vetter's
> now classic treatise on ioctls:
> 
> http://blog.ffwll.ch/2013/11/botching-up-ioctls.html
> 
> A smaller number of us have further been marking the __u64 with __user,
> to wit:
> 
> struct uapistruct {
> 	...
> 	__u64 __user myptr;
> 	---
> };

It wouldn't make sense to have this:
	struct uapistruct {
		__u64 __user myptr;
		__u64        anothermember;
	};
In other words, eiter all members are in the user address space
or none are. So, a struct member should not be marked __user
(exactly as for 'const' or 'volatile').

It wouldn't also make sense to move the __user to the whole struct,
giving something like:
	struct uapistruct {
		__u64 myptr;
		__u64 anothermember;
	} __user;
because it's not the type that belong to the user address space
but some specific objects.

Of course, your real problem here is that you're using a __u64 to
store a pointer and then expect this __u64 to have some properties
unique to pointers.


-- Luc Van Oostenryck (sparse hacker)

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: __user with scalar data types
  2017-06-19 16:15 __user with scalar data types Jordan Crouse
  2017-06-19 16:34 ` Al Viro
  2017-06-19 19:27 ` Luc Van Oostenryck
@ 2017-06-19 20:32 ` Luc Van Oostenryck
  2017-06-19 20:46   ` Al Viro
  2 siblings, 1 reply; 9+ messages in thread
From: Luc Van Oostenryck @ 2017-06-19 20:32 UTC (permalink / raw)
  To: linux-sparse, linux-kernel, dri-devel

On Mon, Jun 19, 2017 at 10:15:09AM -0600, Jordan Crouse wrote:
> struct uapistruct {
> 	...
> 	__u64 __user myptr;
> 	---
> };
> 
> And then converting it for use in the kernel as such:
> 
> {
> 	void __user *userptr = (void __user *)(uintptr_t)args->myptr;
> 
> 	copy_from_user(local, userptr, size);
> 	...
> }
> 
> The problem is that sparse doesn't like the momentary switch to
> uintptr_t:
> 
> 	warning: dereference of noderef expression

This warning doesn't come from the cast to uintptr_t but
simply from dereferencing the field which can't be dereferenced
since it's marked as '__user'. In other words, doing
'args->myptr' rightfully trigger the warning and no cast
will or should stop that.

Also, you can't expect the '__user' to be transmitted from
'myptr' to the pointer (without taking the address of 'myptr').
It's exactly like 'const int' vs. 'const int *': the '__user' or
the 'const' is not at the same level in the type hierarchy
('const object' vs. 'non-const pointer to const object').


-- Luc Van Oostenryck

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: __user with scalar data types
  2017-06-19 20:32 ` Luc Van Oostenryck
@ 2017-06-19 20:46   ` Al Viro
  2017-06-19 22:39     ` Luc Van Oostenryck
  0 siblings, 1 reply; 9+ messages in thread
From: Al Viro @ 2017-06-19 20:46 UTC (permalink / raw)
  To: Luc Van Oostenryck; +Cc: linux-sparse, linux-kernel, dri-devel

On Mon, Jun 19, 2017 at 10:32:18PM +0200, Luc Van Oostenryck wrote:
> On Mon, Jun 19, 2017 at 10:15:09AM -0600, Jordan Crouse wrote:
> > struct uapistruct {
> > 	...
> > 	__u64 __user myptr;
> > 	---
> > };
> > 
> > And then converting it for use in the kernel as such:
> > 
> > {
> > 	void __user *userptr = (void __user *)(uintptr_t)args->myptr;
> > 
> > 	copy_from_user(local, userptr, size);
> > 	...
> > }
> > 
> > The problem is that sparse doesn't like the momentary switch to
> > uintptr_t:
> > 
> > 	warning: dereference of noderef expression
> 
> This warning doesn't come from the cast to uintptr_t but
> simply from dereferencing the field which can't be dereferenced
> since it's marked as '__user'. In other words, doing
> 'args->myptr' rightfully trigger the warning and no cast
> will or should stop that.
> 
> Also, you can't expect the '__user' to be transmitted from
> 'myptr' to the pointer (without taking the address of 'myptr').
> It's exactly like 'const int' vs. 'const int *': the '__user' or
> the 'const' is not at the same level in the type hierarchy
> ('const object' vs. 'non-const pointer to const object').

Besides, suppose you add a special type for that.  How would it
have to behave, really?  AFAICS, you want something similar to
__bitwise, except that (assuming this type is T)
	T + integer => T
	T - integer => T
	T & integer => integer
	T | integer => T
	T - T => integer (quietly decay to underlying type for both
arguments, then treat as normal -)
	T & T => T (probably, but might be worth a warning)
	T | T => T (ditto)
	comparison - same as for __bitwise
	constant conversion: 0 should convert clean, anything else - a warning
	cast to pointer => warn unless the target type is __user?  But that's
not going to help with cast through uintptr_t...
	?: as usual
	any other arithmetics => warn and decay to underlying integer type

It might be not impossible to implement, but it sure as hell won't be __user
and it'll need careful thinking about the semantics of those annotations.
The outline above is just that - figuring out if there are any nasty corner
cases will take some work.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: __user with scalar data types
  2017-06-19 20:46   ` Al Viro
@ 2017-06-19 22:39     ` Luc Van Oostenryck
  0 siblings, 0 replies; 9+ messages in thread
From: Luc Van Oostenryck @ 2017-06-19 22:39 UTC (permalink / raw)
  To: Al Viro; +Cc: linux-sparse, linux-kernel, dri-devel

On Mon, Jun 19, 2017 at 09:46:37PM +0100, Al Viro wrote:
> On Mon, Jun 19, 2017 at 10:32:18PM +0200, Luc Van Oostenryck wrote:
> > On Mon, Jun 19, 2017 at 10:15:09AM -0600, Jordan Crouse wrote:
> > > struct uapistruct {
> > > 	...
> > > 	__u64 __user myptr;
> > > 	---
> > > };
> > > 
> > > And then converting it for use in the kernel as such:
> > > 
> > > {
> > > 	void __user *userptr = (void __user *)(uintptr_t)args->myptr;
> > > 
> > > 	copy_from_user(local, userptr, size);
> > > 	...
> > > }
> > > 
> > > The problem is that sparse doesn't like the momentary switch to
> > > uintptr_t:
> > > 
> > > 	warning: dereference of noderef expression
> > 
> > This warning doesn't come from the cast to uintptr_t but
> > simply from dereferencing the field which can't be dereferenced
> > since it's marked as '__user'. In other words, doing
> > 'args->myptr' rightfully trigger the warning and no cast
> > will or should stop that.
> > 
> > Also, you can't expect the '__user' to be transmitted from
> > 'myptr' to the pointer (without taking the address of 'myptr').
> > It's exactly like 'const int' vs. 'const int *': the '__user' or
> > the 'const' is not at the same level in the type hierarchy
> > ('const object' vs. 'non-const pointer to const object').
> 
> Besides, suppose you add a special type for that.  How would it
> have to behave, really?  AFAICS, you want something similar to
> __bitwise, except that (assuming this type is T)
> 	T + integer => T
> 	T - integer => T
> 	T & integer => integer
> 	T | integer => T
> 	T - T => integer (quietly decay to underlying type for both
> arguments, then treat as normal -)
> 	T & T => T (probably, but might be worth a warning)
> 	T | T => T (ditto)
> 	comparison - same as for __bitwise
> 	constant conversion: 0 should convert clean, anything else - a warning
> 	cast to pointer => warn unless the target type is __user?  But that's
> not going to help with cast through uintptr_t...
> 	?: as usual
> 	any other arithmetics => warn and decay to underlying integer type

And how it should behave with typeof()?
Because it's already unclear to me what should be the result of:
	typeof(X {__user,__noderef,__nocast,__bitwise} [*])
and I don't think sparse do the right thing with this.

That said, I'm of the opinion that simply thinking about implementing this
special type is close to a capital sin.

-- Luc

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: __user with scalar data types
  2017-06-19 16:34 ` Al Viro
@ 2017-06-20  7:12   ` Daniel Vetter
  2017-06-20  7:42     ` Gerd Hoffmann
  0 siblings, 1 reply; 9+ messages in thread
From: Daniel Vetter @ 2017-06-20  7:12 UTC (permalink / raw)
  To: Al Viro, Clark, Rob, Gerd Hoffmann
  Cc: linux-sparse, Linux Kernel Mailing List, dri-devel

On Mon, Jun 19, 2017 at 6:34 PM, Al Viro <viro@zeniv.linux.org.uk> wrote:
> On Mon, Jun 19, 2017 at 10:15:09AM -0600, Jordan Crouse wrote:
>
>> Which raised a bikeshed debate over whether it is appropriate to mark a scalar
>> type as __user.  My opinion is that it is appropriate because __user should mark
>> user memory regardless of the container.
>
> What the hell?  __user is a qualifier like const, volatile, etc.  It's a property
> of *pointer* *type*.  Not some nebulous "marks userland memory" thing.
>
>> I'm looking for opinions or semi-authoritative edicts to determine if we should
>> either start changing our uapi headers or go off and try to figure out how to
>> make sparse understand this particular usage.
>
> Stop cargo-culting, please.

Yep that's cargo-culted, but from a quick grep only msm and qxl
headers do this (the other __user annotations in uapi/drm are for
pointers, where it's correct). Adding those maintainers.

Also, if you use u64_to_user_ptr helper macro sparse should have
caught this (if not we'd need to improve the macro).
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: __user with scalar data types
  2017-06-20  7:12   ` Daniel Vetter
@ 2017-06-20  7:42     ` Gerd Hoffmann
  2017-06-20  8:37       ` Daniel Vetter
  0 siblings, 1 reply; 9+ messages in thread
From: Gerd Hoffmann @ 2017-06-20  7:42 UTC (permalink / raw)
  To: Daniel Vetter, Al Viro, Clark, Rob
  Cc: linux-sparse, Linux Kernel Mailing List, dri-devel

[-- Attachment #1: Type: text/plain, Size: 470 bytes --]

  Hi,

> Yep that's cargo-culted, but from a quick grep only msm and qxl
> headers do this (the other __user annotations in uapi/drm are for
> pointers, where it's correct). Adding those maintainers.

Yep, those looks pointless indeed.

> Also, if you use u64_to_user_ptr helper macro sparse should have
> caught this (if not we'd need to improve the macro).

And qxl should actually use it.

Fix attached (compile-tested only so far), does that look ok?

cheers,
  Gerd

[-- Attachment #2: Type: text/x-patch, Size: 2827 bytes --]

diff --git a/include/uapi/drm/qxl_drm.h b/include/uapi/drm/qxl_drm.h
index 7eef422130..880999d2d8 100644
--- a/include/uapi/drm/qxl_drm.h
+++ b/include/uapi/drm/qxl_drm.h
@@ -80,8 +80,8 @@ struct drm_qxl_reloc {
 };
 
 struct drm_qxl_command {
-	__u64	 __user command; /* void* */
-	__u64	 __user relocs; /* struct drm_qxl_reloc* */
+	__u64		command; /* void* */
+	__u64		relocs; /* struct drm_qxl_reloc* */
 	__u32		type;
 	__u32		command_size;
 	__u32		relocs_num;
@@ -91,7 +91,7 @@ struct drm_qxl_command {
 struct drm_qxl_execbuffer {
 	__u32		flags;		/* for future use */
 	__u32		commands_num;
-	__u64	 __user commands;	/* struct drm_qxl_command* */
+	__u64		commands;	/* struct drm_qxl_command* */
 };
 
 struct drm_qxl_update_area {
diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c
index 0b82a87916..31effed4a3 100644
--- a/drivers/gpu/drm/qxl/qxl_ioctl.c
+++ b/drivers/gpu/drm/qxl/qxl_ioctl.c
@@ -163,7 +163,7 @@ static int qxl_process_single_command(struct qxl_device *qdev,
 		return -EINVAL;
 
 	if (!access_ok(VERIFY_READ,
-		       (void *)(unsigned long)cmd->command,
+		       u64_to_user_ptr(cmd->command),
 		       cmd->command_size))
 		return -EFAULT;
 
@@ -183,7 +183,9 @@ static int qxl_process_single_command(struct qxl_device *qdev,
 
 	/* TODO copy slow path code from i915 */
 	fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE));
-	unwritten = __copy_from_user_inatomic_nocache(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE), (void *)(unsigned long)cmd->command, cmd->command_size);
+	unwritten = __copy_from_user_inatomic_nocache
+		(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE),
+		 u64_to_user_ptr(cmd->command), cmd->command_size);
 
 	{
 		struct qxl_drawable *draw = fb_cmd;
@@ -201,10 +203,9 @@ static int qxl_process_single_command(struct qxl_device *qdev,
 	num_relocs = 0;
 	for (i = 0; i < cmd->relocs_num; ++i) {
 		struct drm_qxl_reloc reloc;
+		struct drm_qxl_reloc __user *u = u64_to_user_ptr(cmd->relocs);
 
-		if (copy_from_user(&reloc,
-				       &((struct drm_qxl_reloc *)(uintptr_t)cmd->relocs)[i],
-				       sizeof(reloc))) {
+		if (copy_from_user(&reloc, u + i, sizeof(reloc))) {
 			ret = -EFAULT;
 			goto out_free_bos;
 		}
@@ -282,10 +283,10 @@ static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data,
 
 	for (cmd_num = 0; cmd_num < execbuffer->commands_num; ++cmd_num) {
 
-		struct drm_qxl_command *commands =
-			(struct drm_qxl_command *)(uintptr_t)execbuffer->commands;
+		struct drm_qxl_command __user *commands =
+			u64_to_user_ptr(execbuffer->commands);
 
-		if (copy_from_user(&user_cmd, &commands[cmd_num],
+		if (copy_from_user(&user_cmd, commands + cmd_num,
 				       sizeof(user_cmd)))
 			return -EFAULT;
 

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: __user with scalar data types
  2017-06-20  7:42     ` Gerd Hoffmann
@ 2017-06-20  8:37       ` Daniel Vetter
  0 siblings, 0 replies; 9+ messages in thread
From: Daniel Vetter @ 2017-06-20  8:37 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Al Viro, Clark, Rob, linux-sparse, Linux Kernel Mailing List, dri-devel

On Tue, Jun 20, 2017 at 9:42 AM, Gerd Hoffmann <kraxel@redhat.com> wrote:
>> Yep that's cargo-culted, but from a quick grep only msm and qxl
>> headers do this (the other __user annotations in uapi/drm are for
>> pointers, where it's correct). Adding those maintainers.
>
> Yep, those looks pointless indeed.
>
>> Also, if you use u64_to_user_ptr helper macro sparse should have
>> caught this (if not we'd need to improve the macro).
>
> And qxl should actually use it.
>
> Fix attached (compile-tested only so far), does that look ok?

Yup. Assuming sparse is happy: Acked-by: me.

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2017-06-20  8:37 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-19 16:15 __user with scalar data types Jordan Crouse
2017-06-19 16:34 ` Al Viro
2017-06-20  7:12   ` Daniel Vetter
2017-06-20  7:42     ` Gerd Hoffmann
2017-06-20  8:37       ` Daniel Vetter
2017-06-19 19:27 ` Luc Van Oostenryck
2017-06-19 20:32 ` Luc Van Oostenryck
2017-06-19 20:46   ` Al Viro
2017-06-19 22:39     ` Luc Van Oostenryck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).