From a001f96a1c93d01e6da68f3de143f01f51b60f87 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 6 Oct 2003 01:15:36 -0700 Subject: [PATCH] [PATCH] UID16 fixes This fixes CONFIG_UID16 problems on x86-64 as discussed earlier. CONFIG_UID16 now only selects the inclusion of kernel/uid16.c, all conversions are triggered dynamically based on type sizes. This allows x86-64 to both include uid16.c for emulation purposes, but not truncate uids to 16bit in sys_newstat. - Replace the old macros from linux/highuid.h with new SET_UID/SET_GID macros that do type checking. Based on Linus' proposal. - Fix everybody to use them. - Clean up some cruft in the x86-64 32bit emulation allowed by this (other 32bit emulations could be cleaned too, but I'm too lazy for that right now) - Add one missing EOVERFLOW check in x86-64 32bit sys_newstat while I was at it. --- arch/mips/kernel/linux32.c | 4 +-- arch/x86_64/ia32/sys_ia32.c | 34 ++++++++++---------- fs/binfmt_elf.c | 4 +-- fs/ncpfs/ioctl.c | 6 ++-- fs/smbfs/inode.c | 4 +-- fs/smbfs/ioctl.c | 9 ++++-- fs/stat.c | 8 ++--- include/asm-x86_64/posix_types.h | 4 +-- include/linux/highuid.h | 53 +++++++++++++++++--------------- ipc/util.c | 8 ++--- 10 files changed, 71 insertions(+), 63 deletions(-) diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c index 186a1933ad728..be36d9c9fb432 100644 --- a/arch/mips/kernel/linux32.c +++ b/arch/mips/kernel/linux32.c @@ -79,8 +79,8 @@ int cp_compat_stat(struct kstat *stat, struct compat_stat *statbuf) tmp.st_ino = stat->ino; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; - SET_STAT_UID(tmp, stat->uid); - SET_STAT_GID(tmp, stat->gid); + SET_UID(tmp.st_uid, stat->uid); + SET_GID(tmp.st_gid, stat->gid); tmp.st_rdev = new_encode_dev(stat->rdev); tmp.st_size = stat->size; tmp.st_atime = stat->atime.tv_sec; diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c index abf9bd34c6fe8..f3846c1c606f2 100644 --- a/arch/x86_64/ia32/sys_ia32.c +++ b/arch/x86_64/ia32/sys_ia32.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -78,28 +79,23 @@ #define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1))) #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -#undef high2lowuid -#undef high2lowgid -#undef low2highuid -#undef low2highgid - -#define high2lowuid(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) -#define high2lowgid(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) -#define low2highuid(uid) ((uid) == (u16)-1) ? (uid_t)-1 : (uid_t)(uid) -#define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid) -extern int overflowuid,overflowgid; - int cp_compat_stat(struct kstat *kbuf, struct compat_stat *ubuf) { + typeof(ubuf->st_uid) uid = 0; + typeof(ubuf->st_gid) gid = 0; + SET_UID(uid, kbuf->uid); + SET_GID(gid, kbuf->gid); if (!old_valid_dev(kbuf->dev) || !old_valid_dev(kbuf->rdev)) return -EOVERFLOW; + if (kbuf->size >= 0x7fffffff) + return -EOVERFLOW; if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct compat_stat)) || __put_user (old_encode_dev(kbuf->dev), &ubuf->st_dev) || __put_user (kbuf->ino, &ubuf->st_ino) || __put_user (kbuf->mode, &ubuf->st_mode) || __put_user (kbuf->nlink, &ubuf->st_nlink) || - __put_user (kbuf->uid, &ubuf->st_uid) || - __put_user (kbuf->gid, &ubuf->st_gid) || + __put_user (uid, &ubuf->st_uid) || + __put_user (gid, &ubuf->st_gid) || __put_user (old_encode_dev(kbuf->rdev), &ubuf->st_rdev) || __put_user (kbuf->size, &ubuf->st_size) || __put_user (kbuf->atime.tv_sec, &ubuf->st_atime) || @@ -120,14 +116,18 @@ int cp_compat_stat(struct kstat *kbuf, struct compat_stat *ubuf) static int cp_stat64(struct stat64 *ubuf, struct kstat *stat) { + typeof(ubuf->st_uid) uid = 0; + typeof(ubuf->st_gid) gid = 0; + SET_UID(uid, stat->uid); + SET_GID(gid, stat->gid); if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct stat64)) || __put_user(huge_encode_dev(stat->dev), &ubuf->st_dev) || __put_user (stat->ino, &ubuf->__st_ino) || __put_user (stat->ino, &ubuf->st_ino) || __put_user (stat->mode, &ubuf->st_mode) || __put_user (stat->nlink, &ubuf->st_nlink) || - __put_user (stat->uid, &ubuf->st_uid) || - __put_user (stat->gid, &ubuf->st_gid) || + __put_user (uid, &ubuf->st_uid) || + __put_user (gid, &ubuf->st_gid) || __put_user (huge_encode_dev(stat->rdev), &ubuf->st_rdev) || __put_user (stat->size, &ubuf->st_size) || __put_user (stat->atime.tv_sec, &ubuf->st_atime) || @@ -1701,8 +1701,8 @@ static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) &arg32->ca32_export.ex32_anon_uid); err |= __get_user(karg->ca_export.ex_anon_gid, &arg32->ca32_export.ex32_anon_gid); - karg->ca_export.ex_anon_uid = high2lowuid(karg->ca_export.ex_anon_uid); - karg->ca_export.ex_anon_gid = high2lowgid(karg->ca_export.ex_anon_gid); + SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid); + SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid); return err; } diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 505c73aa94eed..e8abe0dde5ac7 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1120,8 +1120,8 @@ static void fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, psinfo->pr_zomb = psinfo->pr_sname == 'Z'; psinfo->pr_nice = task_nice(p); psinfo->pr_flag = p->flags; - psinfo->pr_uid = NEW_TO_OLD_UID(p->uid); - psinfo->pr_gid = NEW_TO_OLD_GID(p->gid); + SET_UID(psinfo->pr_uid, p->uid); + SET_GID(psinfo->pr_gid, p->gid); strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname)); return; diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index fb3e550a9abcc..a25f957ad574d 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -112,7 +112,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, return -EINVAL; } /* TODO: info.addr = server->m.serv_addr; */ - info.mounted_uid = NEW_TO_OLD_UID(server->m.mounted_uid); + SET_UID(info.mounted_uid, server->m.mounted_uid); info.connection = server->connection; info.buffer_size = server->buffer_size; info.volume_number = NCP_FINFO(inode)->volNumber; @@ -637,11 +637,13 @@ outrel: /* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2, so we have this out of switch */ if (cmd == NCP_IOC_GETMOUNTUID) { + __kernel_uid_t uid = 0; if ((permission(inode, MAY_READ, NULL) != 0) && (current->uid != server->m.mounted_uid)) { return -EACCES; } - if (put_user(NEW_TO_OLD_UID(server->m.mounted_uid), (__kernel_uid_t *) arg)) + SET_UID(uid, server->m.mounted_uid); + if (put_user(uid, (__kernel_uid_t *) arg)) return -EFAULT; return 0; } diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 85d97d4cb6d5d..1a22ef00cd25b 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -551,8 +551,8 @@ int smb_fill_super(struct super_block *sb, void *raw_data, int silent) if (ver == SMB_MOUNT_OLDVERSION) { mnt->version = oldmnt->version; - mnt->uid = OLD_TO_NEW_UID(oldmnt->uid); - mnt->gid = OLD_TO_NEW_GID(oldmnt->gid); + SET_UID(mnt->uid, oldmnt->uid); + SET_GID(mnt->gid, oldmnt->gid); mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG; mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR; diff --git a/fs/smbfs/ioctl.c b/fs/smbfs/ioctl.c index 235f583d953c8..4b48899eac230 100644 --- a/fs/smbfs/ioctl.c +++ b/fs/smbfs/ioctl.c @@ -31,12 +31,15 @@ smb_ioctl(struct inode *inode, struct file *filp, int result = -EINVAL; switch (cmd) { + uid16_t uid16 = 0; + uid_t uid32 = 0; case SMB_IOC_GETMOUNTUID: - result = put_user(NEW_TO_OLD_UID(server->mnt->mounted_uid), - (uid16_t *) arg); + SET_UID(uid16, server->mnt->mounted_uid); + result = put_user(uid16, (uid16_t *) arg); break; case SMB_IOC_GETMOUNTUID32: - result = put_user(server->mnt->mounted_uid, (uid_t *) arg); + SET_UID(uid32, server->mnt->mounted_uid); + result = put_user(uid32, (uid_t *) arg); break; case SMB_IOC_NEWCONN: diff --git a/fs/stat.c b/fs/stat.c index 7e541b066c5e7..617e75f0e94f1 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -121,8 +121,8 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta tmp.st_ino = stat->ino; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; - SET_OLDSTAT_UID(tmp, stat->uid); - SET_OLDSTAT_GID(tmp, stat->gid); + SET_UID(tmp.st_uid, stat->uid); + SET_GID(tmp.st_gid, stat->gid); tmp.st_rdev = old_encode_dev(stat->rdev); #if BITS_PER_LONG == 32 if (stat->size > MAX_NON_LFS) @@ -189,8 +189,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) tmp.st_ino = stat->ino; tmp.st_mode = stat->mode; tmp.st_nlink = stat->nlink; - SET_STAT_UID(tmp, stat->uid); - SET_STAT_GID(tmp, stat->gid); + SET_UID(tmp.st_uid, stat->uid); + SET_GID(tmp.st_gid, stat->gid); #if BITS_PER_LONG == 32 tmp.st_rdev = old_encode_dev(stat->rdev); #else diff --git a/include/asm-x86_64/posix_types.h b/include/asm-x86_64/posix_types.h index b28ed4955fa3e..9926aa43775b8 100644 --- a/include/asm-x86_64/posix_types.h +++ b/include/asm-x86_64/posix_types.h @@ -36,8 +36,8 @@ typedef struct { int val[2]; } __kernel_fsid_t; -typedef __kernel_uid_t __kernel_old_uid_t; -typedef __kernel_gid_t __kernel_old_gid_t; +typedef unsigned short __kernel_old_uid_t; +typedef unsigned short __kernel_old_gid_t; typedef __kernel_uid_t __kernel_uid32_t; typedef __kernel_gid_t __kernel_gid32_t; diff --git a/include/linux/highuid.h b/include/linux/highuid.h index 9d0bdcbc2080d..3ca170db446c9 100644 --- a/include/linux/highuid.h +++ b/include/linux/highuid.h @@ -35,6 +35,9 @@ extern int overflowuid; extern int overflowgid; +extern void __bad_uid(void); +extern void __bad_gid(void); + #define DEFAULT_OVERFLOWUID 65534 #define DEFAULT_OVERFLOWGID 65534 @@ -50,36 +53,36 @@ extern int overflowgid; #define low2highuid(uid) ((uid) == (old_uid_t)-1 ? (uid_t)-1 : (uid_t)(uid)) #define low2highgid(gid) ((gid) == (old_gid_t)-1 ? (gid_t)-1 : (gid_t)(gid)) -/* Avoid extra ifdefs with these macros */ - -#define SET_UID16(var, uid) var = high2lowuid(uid) -#define SET_GID16(var, gid) var = high2lowgid(gid) -#define NEW_TO_OLD_UID(uid) high2lowuid(uid) -#define NEW_TO_OLD_GID(gid) high2lowgid(gid) -#define OLD_TO_NEW_UID(uid) low2highuid(uid) -#define OLD_TO_NEW_GID(gid) low2highgid(gid) - -/* specific to fs/stat.c */ -#define SET_OLDSTAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) -#define SET_OLDSTAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) -#define SET_STAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) -#define SET_STAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) +/* uid/gid input should be always 32bit uid_t */ +#define SET_UID(var, uid) \ + do { \ + if (sizeof(var) == sizeof(old_uid_t)) (var) = high2lowuid(uid); \ + else if (sizeof(var) >= sizeof(uid)) (var) = (uid); \ + else __bad_uid(); \ + } while(0) + +#define SET_GID(var, gid) \ + do { \ + if (sizeof(var) == sizeof(old_gid_t)) (var) = high2lowgid(gid); \ + else if (sizeof(var) >= sizeof(gid)) (var) = (gid); \ + else __bad_gid(); \ + } while(0) #else -#define SET_UID16(var, uid) do { ; } while (0) -#define SET_GID16(var, gid) do { ; } while (0) -#define NEW_TO_OLD_UID(uid) (uid) -#define NEW_TO_OLD_GID(gid) (gid) -#define OLD_TO_NEW_UID(uid) (uid) -#define OLD_TO_NEW_GID(gid) (gid) +#define SET_UID(var,uid) \ + do { \ + if (sizeof(var) < sizeof(uid)) __bad_uid(); \ + (var) = (uid); \ + } while (0) -#define SET_OLDSTAT_UID(stat, uid) (stat).st_uid = (uid) -#define SET_OLDSTAT_GID(stat, gid) (stat).st_gid = (gid) -#define SET_STAT_UID(stat, uid) (stat).st_uid = (uid) -#define SET_STAT_GID(stat, gid) (stat).st_gid = (gid) +#define SET_GID(var,gid) \ + do { \ + if (sizeof(var) < sizeof(gid)) __bad_gid(); \ + (var) = (gid); \ + } while (0); -#endif /* CONFIG_UID16 */ +#endif /* !CONFIG_UID16 */ /* diff --git a/ipc/util.c b/ipc/util.c index 07bcb8d80a722..806b2d7084f16 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -421,10 +421,10 @@ void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out) void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out) { out->key = in->key; - out->uid = NEW_TO_OLD_UID(in->uid); - out->gid = NEW_TO_OLD_GID(in->gid); - out->cuid = NEW_TO_OLD_UID(in->cuid); - out->cgid = NEW_TO_OLD_GID(in->cgid); + SET_UID(out->uid, in->uid); + SET_GID(out->gid, in->gid); + SET_UID(out->cuid, in->cuid); + SET_GID(out->cgid, in->cgid); out->mode = in->mode; out->seq = in->seq; } -- 2.53.0