2006-01-12 06:02:50

by Shaun Pereira

[permalink] [raw]
Subject: [PATCH 2/4 - 2.6.15]net: 32 bit (socket layer) ioctl emulation for 64 bit kernels

The second part of this series.

This routine is needed by the x25 module (32-64 bit patch), as
recommended it has been added to compat.c

diff -uprN -X dontdiff linux-2.6.15-vanilla/include/net/compat.h
linux-2.6.15/include/net/compat.h
--- linux-2.6.15-vanilla/include/net/compat.h 2006-01-03
14:21:10.000000000 +1100
+++ linux-2.6.15/include/net/compat.h 2006-01-12 16:01:09.000000000
+1100
@@ -23,6 +23,8 @@ struct compat_cmsghdr {
compat_int_t cmsg_type;
};

+extern int compat_sock_get_timestamp(struct sock *, struct timeval
__user *);
+
#else /* defined(CONFIG_COMPAT) */
#define compat_msghdr msghdr /* to avoid compiler warnings */
#endif /* defined(CONFIG_COMPAT) */
diff -uprN -X dontdiff linux-2.6.15-vanilla/net/compat.c
linux-2.6.15/net/compat.c
--- linux-2.6.15-vanilla/net/compat.c 2006-01-03 14:21:10.000000000
+1100
+++ linux-2.6.15/net/compat.c 2006-01-12 16:01:09.000000000 +1100
@@ -503,6 +503,20 @@ static int do_get_sock_timeout(int fd, i
return err;
}

+int compat_sock_get_timestamp(struct sock *sk, struct timeval __user
*userstamp)
+{
+ struct compat_timeval __user *ctv;
+ ctv = (struct compat_timeval __user*) userstamp;
+ if(!sock_flag(sk, SOCK_TIMESTAMP))
+ sock_enable_timestamp(sk);
+ if(sk->sk_stamp.tv_sec == -1)
+ return -ENOENT;
+ if(sk->sk_stamp.tv_sec == 0)
+ do_gettimeofday(&sk->sk_stamp);
+ return copy_to_user(ctv, &sk->sk_stamp, sizeof(struct
compat_timeval)) ?
+ -EFAULT : 0;
+}
+
asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
char __user *optval, int __user *optlen)
{
@@ -602,3 +616,5 @@ asmlinkage long compat_sys_socketcall(in
}
return ret;
}
+
+EXPORT_SYMBOL(compat_sock_get_timestamp);






2006-01-12 19:24:23

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 2/4 - 2.6.15]net: 32 bit (socket layer) ioctl emulation for 64 bit kernels

On Thursday 12 January 2006 06:02, Shaun Pereira wrote:
> +int compat_sock_get_timestamp(struct sock *sk, struct timeval __user
> *userstamp)
> +{
> +???????struct compat_timeval __user *ctv;
> +???????ctv = (struct compat_timeval __user*) userstamp;
> +???????if(!sock_flag(sk, SOCK_TIMESTAMP))
> +???????????????sock_enable_timestamp(sk);
> +???????if(sk->sk_stamp.tv_sec == -1)
> +???????????????return -ENOENT;
> +???????if(sk->sk_stamp.tv_sec == 0)
> +???????????????do_gettimeofday(&sk->sk_stamp);
> +???????return copy_to_user(ctv, &sk->sk_stamp, sizeof(struct
> compat_timeval)) ?
> +???????????????????????-EFAULT : 0;
> +}

This looks wrong, you're not doing any conversion here.
You cannot just copy sk_stamp to ctv, they are not compatible.
See compat_sys_gettimeofday on how to copy a struct timeval
correctly.

The other patches look good.

Arnd <><

2006-01-13 03:15:42

by Shaun Pereira

[permalink] [raw]
Subject: Re: [PATCH 2/4 - 2.6.15]net: 32 bit (socket layer) ioctl emulation for 64 bit kernels

Hi Arnd
Thank you for reviewing that bit of code.
I had a look at compat_sys_gettimeofday and sys32_gettimeofday codes.
They seem to work in a similar way, casting a pointer to the structure
from user space to a compat_timeval type.

But to make sure I have tested the routine by forcing the sk-
>sk_stamp.tv_sec value to 0 in the x25_module ( for testing purposes
only, as it is initialised to -1). Now when
I make a 32 bit userspace SIOCGSTAMP ioctl to the 64 bit kernel I should
get the current time back in user space. This seems to work, the ioctl
returns the system time (just after TEST6:)

So I have left the patch as is for now. However if necessary to use
the element-by-element __put_user routine as in put_tv32, then I can
make the change, just let me know.

Lastly, if anyone following this mail can help with adding this into the
next release, that would be really helpful. We are building a network
management application on linux for a telco and while they have
the support of the SuSE's and Redhat's, any patches accepted by the
kernel community makes a difference, besides saving us from building
custom patches. (BTW, the application used to run on HP-UX, we are now
porting it to linux).

Many Thanks
Shaun
------------------------------
ghostview:/home/spereira/x25_32/src/func_tests/server # uname -a
Linux ghostview 2.6.15-smp #9 SMP Fri Jan 13 12:43:27 EST 2006 x86_64
x86_64 x86_64 GNU/Linux
ghostview:/home/spereira/x25_32/src/func_tests/server # file server
server: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for
GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped
ghostview:/home/spereira/x25_32/src/func_tests/server # ./server
usage: server <local X.121 address> <interface name>
ghostview:/home/spereira/x25_32/src/func_tests/server # ./server
05052384500000 x25tap0
TEST1: create socket : passed
TEST2: bind socket : passed
TEST3: set subscription: passed
TEST4: set facilities: passed

**************
Window size in = 2
Window size out = 2
Packet size in = 7
Packet size out = 7
Reverse flag = 00
Throughput = DD
**************
TEST5: set call user data on listen passed
cud[ 0 ] = 02
cud[ 1 ] = 03
TEST6: set matchlength on listen socket handle passed
The time stamp is Fri Jan 13 13:37:17 2006
TEST7: listen on socket: passed
Waiting for X25 packets
-----------------------------------------------------------





On Thu, 2006-01-12 at 19:24 +0000, Arnd Bergmann wrote:
> On Thursday 12 January 2006 06:02, Shaun Pereira wrote:
> > +int compat_sock_get_timestamp(struct sock *sk, struct timeval __user
> > *userstamp)
> > +{
> > + struct compat_timeval __user *ctv;
> > + ctv = (struct compat_timeval __user*) userstamp;
> > + if(!sock_flag(sk, SOCK_TIMESTAMP))
> > + sock_enable_timestamp(sk);
> > + if(sk->sk_stamp.tv_sec == -1)
> > + return -ENOENT;
> > + if(sk->sk_stamp.tv_sec == 0)
> > + do_gettimeofday(&sk->sk_stamp);
> > + return copy_to_user(ctv, &sk->sk_stamp, sizeof(struct
> > compat_timeval)) ?
> > + -EFAULT : 0;
> > +}
>
> This looks wrong, you're not doing any conversion here.
> You cannot just copy sk_stamp to ctv, they are not compatible.
> See compat_sys_gettimeofday on how to copy a struct timeval
> correctly.
>
> The other patches look good.
>
> Arnd <><

2006-01-13 11:47:25

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 2/4 - 2.6.15]net: 32 bit (socket layer) ioctl emulation for 64 bit kernels

On Friday 13 January 2006 03:14, Shaun Pereira wrote:
> Thank you for reviewing that bit of code. ?
> I had a look at compat_sys_gettimeofday and sys32_gettimeofday codes.
> They seem to work in a similar way, casting a pointer to the structure
> from user space to a compat_timeval type.

The part with the case is ok, except that you don't have to write

struct compat_timeval __user *ctv;
ctv = (struct compat_timeval __user*) userstamp;

Instead,

struct compat_timeval __user *ctv = userstamp;

is the more common way to write it. The result is the same, since
userstamp is a 'void __user *'.

> But to make sure I have tested the routine by forcing the sk-
> >sk_stamp.tv_sec value to 0 in the x25_module ( for testing purposes
> only, as it is initialised to -1). Now when
> I make a 32 bit userspace SIOCGSTAMP ioctl to the 64 bit kernel I should
> get the current time back in user space. This seems to work, the ioctl
> returns the system time (just after TEST6:)
>
> So I have left the patch as is for now. However if necessary to use
> the element-by-element __put_user routine as in put_tv32, then I can
> make the change, just let me know.

You need to to exactly that, yes. I'm not sure what exactly you have
tested, but the expected result of your code would be that you see
the sk_stamp.tv_sec value in the output, but not the tv_usec value.

On little-endian system like x86_64, that is not much of a difference
(less than a second) that you might miss in a test case, but on
big-endian, it would be fatal.

The layout of the structures on most systems is

64 bit LE 64 bit BE 32 bit

bytes 0-3 tv_sec low tv_sec high tv_sec low
bytes 4-7 tv_sec high tv_sec low tv_usec low
bytes 8-11 tv_usec low tv_usec high
bytes 12-15 tv_usec high tv_usec low

You code copies the first eight bytes of the 64 bit data structure
into the 32 bit data structure.

Arnd <><

2006-01-16 06:00:43

by Shaun Pereira

[permalink] [raw]
Subject: Re: 32 bit (socket layer) ioctl emulation for 64 bit kernels

Hi Arnd

I have made the changes suggested, and attached it below. I think it
should be good now.
Just a couple of questions if I may.
If I understand correctly from your comments (thanks for that, they are
helpful)
copy_to_user acts like a memcopy for an 'array' of bytes and should not
be used to copy the timeval struct to userspace.
Rather put_user / __put_user macros should be used which allows transfer
of single element values of the structure.
Does that also mean that copy_to_user should not be used in ioctl
calls?

I was wondering if this the compat_sock_get_timestamp function is
needed? If I were to remove the SIOCGSTAMP case from the
compat_x25_ioctl function, then a SIOCGSTAMP ioctl system call would
return -ENOIOCTLCMD which could then be handled by do_siocgstamp
handler in the ioctl32_hash_table? (fs/compat_ioctl.c)
In which case I could remove this patch from the rest of the series.

/Shaun


Index: linux-2.6.15/include/net/compat.h
===================================================================
--- linux-2.6.15.orig/include/net/compat.h
+++ linux-2.6.15/include/net/compat.h
@@ -23,6 +23,8 @@ struct compat_cmsghdr {
compat_int_t cmsg_type;
};

+extern int compat_sock_get_timestamp(struct sock *, struct timeval
__user *);
+
#else /* defined(CONFIG_COMPAT) */
#define compat_msghdr msghdr /* to avoid compiler warnings */
#endif /* defined(CONFIG_COMPAT) */
Index: linux-2.6.15/net/compat.c
===================================================================
--- linux-2.6.15.orig/net/compat.c
+++ linux-2.6.15/net/compat.c
@@ -503,6 +503,25 @@ static int do_get_sock_timeout(int fd, i
return err;
}

+int compat_sock_get_timestamp(struct sock *sk, struct timeval __user
*userstamp)
+{
+ struct compat_timeval __user *ctv
+ = (struct compat_timeval __user*) userstamp;
+ int err = -ENOENT;
+ if(!sock_flag(sk, SOCK_TIMESTAMP))
+ sock_enable_timestamp(sk);
+ if(sk->sk_stamp.tv_sec == -1)
+ return err;
+ if(sk->sk_stamp.tv_sec == 0)
+ do_gettimeofday(&sk->sk_stamp);
+ err = -EFAULT;
+ if(access_ok(VERIFTY_WRITE, ctv, sizeof(*ctv))) {
+ err = __put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec);
+ err != __put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec);
+ }
+ return err;
+}
+
asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
char __user *optval, int __user *optlen)
{
@@ -602,3 +621,5 @@ asmlinkage long compat_sys_socketcall(in
}
return ret;
}
+
+EXPORT_SYMBOL(compat_sock_get_timestamp);


On Fri, 2006-01-13 at 11:46 +0000, Arnd Bergmann wrote:
> On Friday 13 January 2006 03:14, Shaun Pereira wrote:
> > Thank you for reviewing that bit of code.
> > I had a look at compat_sys_gettimeofday and sys32_gettimeofday codes.
> > They seem to work in a similar way, casting a pointer to the structure
> > from user space to a compat_timeval type.
>
> The part with the case is ok, except that you don't have to write
>
> struct compat_timeval __user *ctv;
> ctv = (struct compat_timeval __user*) userstamp;
>
> Instead,
>
> struct compat_timeval __user *ctv = userstamp;
>
> is the more common way to write it. The result is the same, since
> userstamp is a 'void __user *'.
>
> > But to make sure I have tested the routine by forcing the sk-
> > >sk_stamp.tv_sec value to 0 in the x25_module ( for testing purposes
> > only, as it is initialised to -1). Now when
> > I make a 32 bit userspace SIOCGSTAMP ioctl to the 64 bit kernel I should
> > get the current time back in user space. This seems to work, the ioctl
> > returns the system time (just after TEST6:)
> >
> > So I have left the patch as is for now. However if necessary to use
> > the element-by-element __put_user routine as in put_tv32, then I can
> > make the change, just let me know.
>
> You need to to exactly that, yes. I'm not sure what exactly you have
> tested, but the expected result of your code would be that you see
> the sk_stamp.tv_sec value in the output, but not the tv_usec value.
>
> On little-endian system like x86_64, that is not much of a difference
> (less than a second) that you might miss in a test case, but on
> big-endian, it would be fatal.
>
> The layout of the structures on most systems is
>
> 64 bit LE 64 bit BE 32 bit
>
> bytes 0-3 tv_sec low tv_sec high tv_sec low
> bytes 4-7 tv_sec high tv_sec low tv_usec low
> bytes 8-11 tv_usec low tv_usec high
> bytes 12-15 tv_usec high tv_usec low
>
> You code copies the first eight bytes of the 64 bit data structure
> into the 32 bit data structure.
>
> Arnd <><

2006-01-16 06:40:19

by YOSHIFUJI Hideaki

[permalink] [raw]
Subject: Re: 32 bit (socket layer) ioctl emulation for 64 bit kernels

In article <[email protected]> (at Mon, 16 Jan 2006 16:59:20 +1100), Shaun Pereira <[email protected]> says:

> If I understand correctly from your comments (thanks for that, they are
> helpful)
> copy_to_user acts like a memcopy for an 'array' of bytes and should not
> be used to copy the timeval struct to userspace.
> Rather put_user / __put_user macros should be used which allows transfer
> of single element values of the structure.

> +int compat_sock_get_timestamp(struct sock *sk, struct timeval __user
> *userstamp)
> +{
> + struct compat_timeval __user *ctv
> + = (struct compat_timeval __user*) userstamp;
> + int err = -ENOENT;
> + if(!sock_flag(sk, SOCK_TIMESTAMP))
> + sock_enable_timestamp(sk);
> + if(sk->sk_stamp.tv_sec == -1)
> + return err;
> + if(sk->sk_stamp.tv_sec == 0)
> + do_gettimeofday(&sk->sk_stamp);
> + err = -EFAULT;
> + if(access_ok(VERIFTY_WRITE, ctv, sizeof(*ctv))) {
> + err = __put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec);
> + err != __put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec);
> + }
> + return err;
> +}
> +

Hmm, you will copy 32bit of MSB in big-endian.
You should do something like this:

strtuct compat_timeval tvtmp;
:
tvtmp.tv_sec = sk->sk_stamp.tv_sec;
tvtmp.tv_usec = sk->sk_stemp.tv_usec;
return copy_to_user(ctv, &tvtmp, sizeof(tvtmp));

Or, am I miss something?

--yoshfuji

2006-01-16 09:39:53

by Arnd Bergmann

[permalink] [raw]
Subject: Re: 32 bit (socket layer) ioctl emulation for 64 bit kernels

On Monday 16 January 2006 06:59, Shaun Pereira wrote:
>
> I was wondering if this the compat_sock_get_timestamp function is
> needed? If I were to remove the SIOCGSTAMP case from the
> compat_x25_ioctl function, then a SIOCGSTAMP ioctl system call would
> return -ENOIOCTLCMD which could ?then be handled by do_siocgstamp
> handler in the ioctl32_hash_table? (fs/compat_ioctl.c)
> In which case I could remove this patch from the rest of the series.

Yes, that would also work, as I already mentioned (or tried to)
in one of my earlier comments. I would prefer to have this patch
though, because in the long term, I think we should migrate more
stuff away from the hash table and having the function there
means that others can use it as well.

> +???????err = -EFAULT;
> +???????if(access_ok(VERIFTY_WRITE, ctv, sizeof(*ctv))) {
> +???????????????err = __put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec);
> +???????????????err != __put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec);
> +???????}
> +???????return err;
> +}

This copies the correct data down to user space now, but might result
in returning an invalid error code.
In the second line you now have 'err != __put_user(...);', which is
a comparison, not an assignment!
For readability, I would simply write that as:

ret = 0;
if (put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec) |
put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec))
err = -EFAULT;

You can also write it like your code, but with '|' instead of '!', but
that requires the additional knowledge that __put_user can only ever
return '0' or '-EFAULT' itself and that the bitwise or of those is
therefore also one of these two.

Arnd <><