aboutsummaryrefslogtreecommitdiffstats
path: root/fs/compat.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2012-02-12 20:56:29 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2012-02-13 20:45:39 -0500
commitfcf83067bf6eb101a35620d752bd559d473cfbaa (patch)
treea34e89f84bea5bd9193ed12bc49361950bba03a8 /fs/compat.c
parentdcdbed853d9fbb0547b781ba676049b87f54129a (diff)
vfs: fix compat_sys_stat() handling of overflows in st_nlink
Massaged cp_compat_stat() into form closer to cp_new_stat(); the only real issue had been in handling of st_nlink overflows - native 32bit stat(2) returns -EOVERFLOW in such situations, compat one silently loses upper bits. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/compat.c')
-rw-r--r--fs/compat.c56
1 files changed, 25 insertions, 31 deletions
diff --git a/fs/compat.c b/fs/compat.c
index fa9d721ecfee..07880bae28a9 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -131,41 +131,35 @@ asmlinkage long compat_sys_utimes(const char __user *filename, struct compat_tim
131 131
132static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf) 132static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
133{ 133{
134 compat_ino_t ino = stat->ino; 134 struct compat_stat tmp;
135 typeof(ubuf->st_uid) uid = 0;
136 typeof(ubuf->st_gid) gid = 0;
137 int err;
138 135
139 SET_UID(uid, stat->uid); 136 if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev))
140 SET_GID(gid, stat->gid); 137 return -EOVERFLOW;
141 138
142 if ((u64) stat->size > MAX_NON_LFS || 139 memset(&tmp, 0, sizeof(tmp));
143 !old_valid_dev(stat->dev) || 140 tmp.st_dev = old_encode_dev(stat->dev);
144 !old_valid_dev(stat->rdev)) 141 tmp.st_ino = stat->ino;
142 if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
145 return -EOVERFLOW; 143 return -EOVERFLOW;
146 if (sizeof(ino) < sizeof(stat->ino) && ino != stat->ino) 144 tmp.st_mode = stat->mode;
145 tmp.st_nlink = stat->nlink;
146 if (tmp.st_nlink != stat->nlink)
147 return -EOVERFLOW; 147 return -EOVERFLOW;
148 148 SET_UID(tmp.st_uid, stat->uid);
149 if (clear_user(ubuf, sizeof(*ubuf))) 149 SET_GID(tmp.st_gid, stat->gid);
150 return -EFAULT; 150 tmp.st_rdev = old_encode_dev(stat->rdev);
151 151 if ((u64) stat->size > MAX_NON_LFS)
152 err = __put_user(old_encode_dev(stat->dev), &ubuf->st_dev); 152 return -EOVERFLOW;
153 err |= __put_user(ino, &ubuf->st_ino); 153 tmp.st_size = stat->size;
154 err |= __put_user(stat->mode, &ubuf->st_mode); 154 tmp.st_atime = stat->atime.tv_sec;
155 err |= __put_user(stat->nlink, &ubuf->st_nlink); 155 tmp.st_atime_nsec = stat->atime.tv_nsec;
156 err |= __put_user(uid, &ubuf->st_uid); 156 tmp.st_mtime = stat->mtime.tv_sec;
157 err |= __put_user(gid, &ubuf->st_gid); 157 tmp.st_mtime_nsec = stat->mtime.tv_nsec;
158 err |= __put_user(old_encode_dev(stat->rdev), &ubuf->st_rdev); 158 tmp.st_ctime = stat->ctime.tv_sec;
159 err |= __put_user(stat->size, &ubuf->st_size); 159 tmp.st_ctime_nsec = stat->ctime.tv_nsec;
160 err |= __put_user(stat->atime.tv_sec, &ubuf->st_atime); 160 tmp.st_blocks = stat->blocks;
161 err |= __put_user(stat->atime.tv_nsec, &ubuf->st_atime_nsec); 161 tmp.st_blksize = stat->blksize;
162 err |= __put_user(stat->mtime.tv_sec, &ubuf->st_mtime); 162 return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0;
163 err |= __put_user(stat->mtime.tv_nsec, &ubuf->st_mtime_nsec);
164 err |= __put_user(stat->ctime.tv_sec, &ubuf->st_ctime);
165 err |= __put_user(stat->ctime.tv_nsec, &ubuf->st_ctime_nsec);
166 err |= __put_user(stat->blksize, &ubuf->st_blksize);
167 err |= __put_user(stat->blocks, &ubuf->st_blocks);
168 return err;
169} 163}
170 164
171asmlinkage long compat_sys_newstat(const char __user * filename, 165asmlinkage long compat_sys_newstat(const char __user * filename,