diff options
author | Lin Feng Shen <shenlinf@cn.ibm.com> | 2006-05-20 17:59:49 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-05-21 15:59:16 -0400 |
commit | d64b1c878fc1e384ae53d1d40034239bc33848f4 (patch) | |
tree | fe31b26f2db18d021cf04181bd39e08b2beb17f1 | |
parent | 84b3932bf0fd8cdc8c75a5be77e1dded1e6479c6 (diff) |
[PATCH] NFS: fix error handling on access_ok in compat_sys_nfsservctl
Functions compat_nfs_svc_trans, compat_nfs_clnt_trans,
compat_nfs_exp_trans, compat_nfs_getfd_trans and compat_nfs_getfs_trans,
which are called by compat_sys_nfsservctl(fs/compat.c), don't handle the
return value of access_ok properly. access_ok return 1 when the addr is
valid, and 0 when it's not, but these functions have the reversed
understanding. When the address is valid, they always return -EFAULT to
compat_sys_nfsservctl.
An example is to run /usr/sbin/rpc.nfsd(32bit program on Power5). It
doesn't function as expected. strace showes that nfsservctl returns
-EFAULT.
The patch fixes this by correcting the error handling on the return value
of access_ok in the five functions.
Signed-off-by: Lin Feng Shen <shenlinf@cn.ibm.com>
Cc: Trond Myklebust <trond.myklebust@fys.uio.no>
Acked-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | fs/compat.c | 177 |
1 files changed, 92 insertions, 85 deletions
diff --git a/fs/compat.c b/fs/compat.c index 01f39f87f372..b1f64786a613 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -2030,109 +2030,115 @@ union compat_nfsctl_res { | |||
2030 | struct knfsd_fh cr32_getfs; | 2030 | struct knfsd_fh cr32_getfs; |
2031 | }; | 2031 | }; |
2032 | 2032 | ||
2033 | static int compat_nfs_svc_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg) | 2033 | static int compat_nfs_svc_trans(struct nfsctl_arg *karg, |
2034 | { | 2034 | struct compat_nfsctl_arg __user *arg) |
2035 | int err; | 2035 | { |
2036 | 2036 | if (!access_ok(VERIFY_READ, &arg->ca32_svc, sizeof(arg->ca32_svc)) || | |
2037 | err = access_ok(VERIFY_READ, &arg->ca32_svc, sizeof(arg->ca32_svc)); | 2037 | get_user(karg->ca_version, &arg->ca32_version) || |
2038 | err |= get_user(karg->ca_version, &arg->ca32_version); | 2038 | __get_user(karg->ca_svc.svc_port, &arg->ca32_svc.svc32_port) || |
2039 | err |= __get_user(karg->ca_svc.svc_port, &arg->ca32_svc.svc32_port); | 2039 | __get_user(karg->ca_svc.svc_nthreads, |
2040 | err |= __get_user(karg->ca_svc.svc_nthreads, &arg->ca32_svc.svc32_nthreads); | 2040 | &arg->ca32_svc.svc32_nthreads)) |
2041 | return (err) ? -EFAULT : 0; | 2041 | return -EFAULT; |
2042 | return 0; | ||
2042 | } | 2043 | } |
2043 | 2044 | ||
2044 | static int compat_nfs_clnt_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg) | 2045 | static int compat_nfs_clnt_trans(struct nfsctl_arg *karg, |
2045 | { | 2046 | struct compat_nfsctl_arg __user *arg) |
2046 | int err; | 2047 | { |
2047 | 2048 | if (!access_ok(VERIFY_READ, &arg->ca32_client, | |
2048 | err = access_ok(VERIFY_READ, &arg->ca32_client, sizeof(arg->ca32_client)); | 2049 | sizeof(arg->ca32_client)) || |
2049 | err |= get_user(karg->ca_version, &arg->ca32_version); | 2050 | get_user(karg->ca_version, &arg->ca32_version) || |
2050 | err |= __copy_from_user(&karg->ca_client.cl_ident[0], | 2051 | __copy_from_user(&karg->ca_client.cl_ident[0], |
2051 | &arg->ca32_client.cl32_ident[0], | 2052 | &arg->ca32_client.cl32_ident[0], |
2052 | NFSCLNT_IDMAX); | 2053 | NFSCLNT_IDMAX) || |
2053 | err |= __get_user(karg->ca_client.cl_naddr, &arg->ca32_client.cl32_naddr); | 2054 | __get_user(karg->ca_client.cl_naddr, |
2054 | err |= __copy_from_user(&karg->ca_client.cl_addrlist[0], | 2055 | &arg->ca32_client.cl32_naddr) || |
2055 | &arg->ca32_client.cl32_addrlist[0], | 2056 | __copy_from_user(&karg->ca_client.cl_addrlist[0], |
2056 | (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)); | 2057 | &arg->ca32_client.cl32_addrlist[0], |
2057 | err |= __get_user(karg->ca_client.cl_fhkeytype, | 2058 | (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)) || |
2058 | &arg->ca32_client.cl32_fhkeytype); | 2059 | __get_user(karg->ca_client.cl_fhkeytype, |
2059 | err |= __get_user(karg->ca_client.cl_fhkeylen, | 2060 | &arg->ca32_client.cl32_fhkeytype) || |
2060 | &arg->ca32_client.cl32_fhkeylen); | 2061 | __get_user(karg->ca_client.cl_fhkeylen, |
2061 | err |= __copy_from_user(&karg->ca_client.cl_fhkey[0], | 2062 | &arg->ca32_client.cl32_fhkeylen) || |
2062 | &arg->ca32_client.cl32_fhkey[0], | 2063 | __copy_from_user(&karg->ca_client.cl_fhkey[0], |
2063 | NFSCLNT_KEYMAX); | 2064 | &arg->ca32_client.cl32_fhkey[0], |
2065 | NFSCLNT_KEYMAX)) | ||
2066 | return -EFAULT; | ||
2064 | 2067 | ||
2065 | return (err) ? -EFAULT : 0; | 2068 | return 0; |
2066 | } | 2069 | } |
2067 | 2070 | ||
2068 | static int compat_nfs_exp_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg) | 2071 | static int compat_nfs_exp_trans(struct nfsctl_arg *karg, |
2069 | { | 2072 | struct compat_nfsctl_arg __user *arg) |
2070 | int err; | 2073 | { |
2071 | 2074 | if (!access_ok(VERIFY_READ, &arg->ca32_export, | |
2072 | err = access_ok(VERIFY_READ, &arg->ca32_export, sizeof(arg->ca32_export)); | 2075 | sizeof(arg->ca32_export)) || |
2073 | err |= get_user(karg->ca_version, &arg->ca32_version); | 2076 | get_user(karg->ca_version, &arg->ca32_version) || |
2074 | err |= __copy_from_user(&karg->ca_export.ex_client[0], | 2077 | __copy_from_user(&karg->ca_export.ex_client[0], |
2075 | &arg->ca32_export.ex32_client[0], | 2078 | &arg->ca32_export.ex32_client[0], |
2076 | NFSCLNT_IDMAX); | 2079 | NFSCLNT_IDMAX) || |
2077 | err |= __copy_from_user(&karg->ca_export.ex_path[0], | 2080 | __copy_from_user(&karg->ca_export.ex_path[0], |
2078 | &arg->ca32_export.ex32_path[0], | 2081 | &arg->ca32_export.ex32_path[0], |
2079 | NFS_MAXPATHLEN); | 2082 | NFS_MAXPATHLEN) || |
2080 | err |= __get_user(karg->ca_export.ex_dev, | 2083 | __get_user(karg->ca_export.ex_dev, |
2081 | &arg->ca32_export.ex32_dev); | 2084 | &arg->ca32_export.ex32_dev) || |
2082 | err |= __get_user(karg->ca_export.ex_ino, | 2085 | __get_user(karg->ca_export.ex_ino, |
2083 | &arg->ca32_export.ex32_ino); | 2086 | &arg->ca32_export.ex32_ino) || |
2084 | err |= __get_user(karg->ca_export.ex_flags, | 2087 | __get_user(karg->ca_export.ex_flags, |
2085 | &arg->ca32_export.ex32_flags); | 2088 | &arg->ca32_export.ex32_flags) || |
2086 | err |= __get_user(karg->ca_export.ex_anon_uid, | 2089 | __get_user(karg->ca_export.ex_anon_uid, |
2087 | &arg->ca32_export.ex32_anon_uid); | 2090 | &arg->ca32_export.ex32_anon_uid) || |
2088 | err |= __get_user(karg->ca_export.ex_anon_gid, | 2091 | __get_user(karg->ca_export.ex_anon_gid, |
2089 | &arg->ca32_export.ex32_anon_gid); | 2092 | &arg->ca32_export.ex32_anon_gid)) |
2093 | return -EFAULT; | ||
2090 | SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid); | 2094 | SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid); |
2091 | SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid); | 2095 | SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid); |
2092 | 2096 | ||
2093 | return (err) ? -EFAULT : 0; | 2097 | return 0; |
2094 | } | 2098 | } |
2095 | 2099 | ||
2096 | static int compat_nfs_getfd_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg) | 2100 | static int compat_nfs_getfd_trans(struct nfsctl_arg *karg, |
2097 | { | 2101 | struct compat_nfsctl_arg __user *arg) |
2098 | int err; | 2102 | { |
2099 | 2103 | if (!access_ok(VERIFY_READ, &arg->ca32_getfd, | |
2100 | err = access_ok(VERIFY_READ, &arg->ca32_getfd, sizeof(arg->ca32_getfd)); | 2104 | sizeof(arg->ca32_getfd)) || |
2101 | err |= get_user(karg->ca_version, &arg->ca32_version); | 2105 | get_user(karg->ca_version, &arg->ca32_version) || |
2102 | err |= __copy_from_user(&karg->ca_getfd.gd_addr, | 2106 | __copy_from_user(&karg->ca_getfd.gd_addr, |
2103 | &arg->ca32_getfd.gd32_addr, | 2107 | &arg->ca32_getfd.gd32_addr, |
2104 | (sizeof(struct sockaddr))); | 2108 | (sizeof(struct sockaddr))) || |
2105 | err |= __copy_from_user(&karg->ca_getfd.gd_path, | 2109 | __copy_from_user(&karg->ca_getfd.gd_path, |
2106 | &arg->ca32_getfd.gd32_path, | 2110 | &arg->ca32_getfd.gd32_path, |
2107 | (NFS_MAXPATHLEN+1)); | 2111 | (NFS_MAXPATHLEN+1)) || |
2108 | err |= __get_user(karg->ca_getfd.gd_version, | 2112 | __get_user(karg->ca_getfd.gd_version, |
2109 | &arg->ca32_getfd.gd32_version); | 2113 | &arg->ca32_getfd.gd32_version)) |
2114 | return -EFAULT; | ||
2110 | 2115 | ||
2111 | return (err) ? -EFAULT : 0; | 2116 | return 0; |
2112 | } | 2117 | } |
2113 | 2118 | ||
2114 | static int compat_nfs_getfs_trans(struct nfsctl_arg *karg, struct compat_nfsctl_arg __user *arg) | 2119 | static int compat_nfs_getfs_trans(struct nfsctl_arg *karg, |
2120 | struct compat_nfsctl_arg __user *arg) | ||
2115 | { | 2121 | { |
2116 | int err; | 2122 | if (!access_ok(VERIFY_READ,&arg->ca32_getfs,sizeof(arg->ca32_getfs)) || |
2117 | 2123 | get_user(karg->ca_version, &arg->ca32_version) || | |
2118 | err = access_ok(VERIFY_READ, &arg->ca32_getfs, sizeof(arg->ca32_getfs)); | 2124 | __copy_from_user(&karg->ca_getfs.gd_addr, |
2119 | err |= get_user(karg->ca_version, &arg->ca32_version); | 2125 | &arg->ca32_getfs.gd32_addr, |
2120 | err |= __copy_from_user(&karg->ca_getfs.gd_addr, | 2126 | (sizeof(struct sockaddr))) || |
2121 | &arg->ca32_getfs.gd32_addr, | 2127 | __copy_from_user(&karg->ca_getfs.gd_path, |
2122 | (sizeof(struct sockaddr))); | 2128 | &arg->ca32_getfs.gd32_path, |
2123 | err |= __copy_from_user(&karg->ca_getfs.gd_path, | 2129 | (NFS_MAXPATHLEN+1)) || |
2124 | &arg->ca32_getfs.gd32_path, | 2130 | __get_user(karg->ca_getfs.gd_maxlen, |
2125 | (NFS_MAXPATHLEN+1)); | 2131 | &arg->ca32_getfs.gd32_maxlen)) |
2126 | err |= __get_user(karg->ca_getfs.gd_maxlen, | 2132 | return -EFAULT; |
2127 | &arg->ca32_getfs.gd32_maxlen); | ||
2128 | 2133 | ||
2129 | return (err) ? -EFAULT : 0; | 2134 | return 0; |
2130 | } | 2135 | } |
2131 | 2136 | ||
2132 | /* This really doesn't need translations, we are only passing | 2137 | /* This really doesn't need translations, we are only passing |
2133 | * back a union which contains opaque nfs file handle data. | 2138 | * back a union which contains opaque nfs file handle data. |
2134 | */ | 2139 | */ |
2135 | static int compat_nfs_getfh_res_trans(union nfsctl_res *kres, union compat_nfsctl_res __user *res) | 2140 | static int compat_nfs_getfh_res_trans(union nfsctl_res *kres, |
2141 | union compat_nfsctl_res __user *res) | ||
2136 | { | 2142 | { |
2137 | int err; | 2143 | int err; |
2138 | 2144 | ||
@@ -2141,8 +2147,9 @@ static int compat_nfs_getfh_res_trans(union nfsctl_res *kres, union compat_nfsct | |||
2141 | return (err) ? -EFAULT : 0; | 2147 | return (err) ? -EFAULT : 0; |
2142 | } | 2148 | } |
2143 | 2149 | ||
2144 | asmlinkage long compat_sys_nfsservctl(int cmd, struct compat_nfsctl_arg __user *arg, | 2150 | asmlinkage long compat_sys_nfsservctl(int cmd, |
2145 | union compat_nfsctl_res __user *res) | 2151 | struct compat_nfsctl_arg __user *arg, |
2152 | union compat_nfsctl_res __user *res) | ||
2146 | { | 2153 | { |
2147 | struct nfsctl_arg *karg; | 2154 | struct nfsctl_arg *karg; |
2148 | union nfsctl_res *kres; | 2155 | union nfsctl_res *kres; |