Commit a001f96a authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds

[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.
parent d37a8f36
...@@ -79,8 +79,8 @@ int cp_compat_stat(struct kstat *stat, struct compat_stat *statbuf) ...@@ -79,8 +79,8 @@ int cp_compat_stat(struct kstat *stat, struct compat_stat *statbuf)
tmp.st_ino = stat->ino; tmp.st_ino = stat->ino;
tmp.st_mode = stat->mode; tmp.st_mode = stat->mode;
tmp.st_nlink = stat->nlink; tmp.st_nlink = stat->nlink;
SET_STAT_UID(tmp, stat->uid); SET_UID(tmp.st_uid, stat->uid);
SET_STAT_GID(tmp, stat->gid); SET_GID(tmp.st_gid, stat->gid);
tmp.st_rdev = new_encode_dev(stat->rdev); tmp.st_rdev = new_encode_dev(stat->rdev);
tmp.st_size = stat->size; tmp.st_size = stat->size;
tmp.st_atime = stat->atime.tv_sec; tmp.st_atime = stat->atime.tv_sec;
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/highuid.h>
#include <asm/mman.h> #include <asm/mman.h>
#include <asm/types.h> #include <asm/types.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -78,28 +79,23 @@ ...@@ -78,28 +79,23 @@
#define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1))) #define ROUND_UP(x,a) ((__typeof__(x))(((unsigned long)(x) + ((a) - 1)) & ~((a) - 1)))
#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) #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) 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)) if (!old_valid_dev(kbuf->dev) || !old_valid_dev(kbuf->rdev))
return -EOVERFLOW; return -EOVERFLOW;
if (kbuf->size >= 0x7fffffff)
return -EOVERFLOW;
if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct compat_stat)) || if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct compat_stat)) ||
__put_user (old_encode_dev(kbuf->dev), &ubuf->st_dev) || __put_user (old_encode_dev(kbuf->dev), &ubuf->st_dev) ||
__put_user (kbuf->ino, &ubuf->st_ino) || __put_user (kbuf->ino, &ubuf->st_ino) ||
__put_user (kbuf->mode, &ubuf->st_mode) || __put_user (kbuf->mode, &ubuf->st_mode) ||
__put_user (kbuf->nlink, &ubuf->st_nlink) || __put_user (kbuf->nlink, &ubuf->st_nlink) ||
__put_user (kbuf->uid, &ubuf->st_uid) || __put_user (uid, &ubuf->st_uid) ||
__put_user (kbuf->gid, &ubuf->st_gid) || __put_user (gid, &ubuf->st_gid) ||
__put_user (old_encode_dev(kbuf->rdev), &ubuf->st_rdev) || __put_user (old_encode_dev(kbuf->rdev), &ubuf->st_rdev) ||
__put_user (kbuf->size, &ubuf->st_size) || __put_user (kbuf->size, &ubuf->st_size) ||
__put_user (kbuf->atime.tv_sec, &ubuf->st_atime) || __put_user (kbuf->atime.tv_sec, &ubuf->st_atime) ||
...@@ -120,14 +116,18 @@ int cp_compat_stat(struct kstat *kbuf, struct compat_stat *ubuf) ...@@ -120,14 +116,18 @@ int cp_compat_stat(struct kstat *kbuf, struct compat_stat *ubuf)
static int static int
cp_stat64(struct stat64 *ubuf, struct kstat *stat) 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)) || if (verify_area(VERIFY_WRITE, ubuf, sizeof(struct stat64)) ||
__put_user(huge_encode_dev(stat->dev), &ubuf->st_dev) || __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->ino, &ubuf->st_ino) || __put_user (stat->ino, &ubuf->st_ino) ||
__put_user (stat->mode, &ubuf->st_mode) || __put_user (stat->mode, &ubuf->st_mode) ||
__put_user (stat->nlink, &ubuf->st_nlink) || __put_user (stat->nlink, &ubuf->st_nlink) ||
__put_user (stat->uid, &ubuf->st_uid) || __put_user (uid, &ubuf->st_uid) ||
__put_user (stat->gid, &ubuf->st_gid) || __put_user (gid, &ubuf->st_gid) ||
__put_user (huge_encode_dev(stat->rdev), &ubuf->st_rdev) || __put_user (huge_encode_dev(stat->rdev), &ubuf->st_rdev) ||
__put_user (stat->size, &ubuf->st_size) || __put_user (stat->size, &ubuf->st_size) ||
__put_user (stat->atime.tv_sec, &ubuf->st_atime) || __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) ...@@ -1701,8 +1701,8 @@ static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
&arg32->ca32_export.ex32_anon_uid); &arg32->ca32_export.ex32_anon_uid);
err |= __get_user(karg->ca_export.ex_anon_gid, err |= __get_user(karg->ca_export.ex_anon_gid,
&arg32->ca32_export.ex32_anon_gid); &arg32->ca32_export.ex32_anon_gid);
karg->ca_export.ex_anon_uid = high2lowuid(karg->ca_export.ex_anon_uid); SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid);
karg->ca_export.ex_anon_gid = high2lowgid(karg->ca_export.ex_anon_gid); SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid);
return err; return err;
} }
......
...@@ -1120,8 +1120,8 @@ static void fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, ...@@ -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_zomb = psinfo->pr_sname == 'Z';
psinfo->pr_nice = task_nice(p); psinfo->pr_nice = task_nice(p);
psinfo->pr_flag = p->flags; psinfo->pr_flag = p->flags;
psinfo->pr_uid = NEW_TO_OLD_UID(p->uid); SET_UID(psinfo->pr_uid, p->uid);
psinfo->pr_gid = NEW_TO_OLD_GID(p->gid); SET_GID(psinfo->pr_gid, p->gid);
strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname)); strncpy(psinfo->pr_fname, p->comm, sizeof(psinfo->pr_fname));
return; return;
......
...@@ -112,7 +112,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, ...@@ -112,7 +112,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
return -EINVAL; return -EINVAL;
} }
/* TODO: info.addr = server->m.serv_addr; */ /* 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.connection = server->connection;
info.buffer_size = server->buffer_size; info.buffer_size = server->buffer_size;
info.volume_number = NCP_FINFO(inode)->volNumber; info.volume_number = NCP_FINFO(inode)->volNumber;
...@@ -637,11 +637,13 @@ int ncp_ioctl(struct inode *inode, struct file *filp, ...@@ -637,11 +637,13 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
/* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2, /* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2,
so we have this out of switch */ so we have this out of switch */
if (cmd == NCP_IOC_GETMOUNTUID) { if (cmd == NCP_IOC_GETMOUNTUID) {
__kernel_uid_t uid = 0;
if ((permission(inode, MAY_READ, NULL) != 0) if ((permission(inode, MAY_READ, NULL) != 0)
&& (current->uid != server->m.mounted_uid)) { && (current->uid != server->m.mounted_uid)) {
return -EACCES; 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 -EFAULT;
return 0; return 0;
} }
......
...@@ -551,8 +551,8 @@ int smb_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -551,8 +551,8 @@ int smb_fill_super(struct super_block *sb, void *raw_data, int silent)
if (ver == SMB_MOUNT_OLDVERSION) { if (ver == SMB_MOUNT_OLDVERSION) {
mnt->version = oldmnt->version; mnt->version = oldmnt->version;
mnt->uid = OLD_TO_NEW_UID(oldmnt->uid); SET_UID(mnt->uid, oldmnt->uid);
mnt->gid = OLD_TO_NEW_GID(oldmnt->gid); SET_GID(mnt->gid, oldmnt->gid);
mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG; mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG;
mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR; mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR;
......
...@@ -31,12 +31,15 @@ smb_ioctl(struct inode *inode, struct file *filp, ...@@ -31,12 +31,15 @@ smb_ioctl(struct inode *inode, struct file *filp,
int result = -EINVAL; int result = -EINVAL;
switch (cmd) { switch (cmd) {
uid16_t uid16 = 0;
uid_t uid32 = 0;
case SMB_IOC_GETMOUNTUID: case SMB_IOC_GETMOUNTUID:
result = put_user(NEW_TO_OLD_UID(server->mnt->mounted_uid), SET_UID(uid16, server->mnt->mounted_uid);
(uid16_t *) arg); result = put_user(uid16, (uid16_t *) arg);
break; break;
case SMB_IOC_GETMOUNTUID32: 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; break;
case SMB_IOC_NEWCONN: case SMB_IOC_NEWCONN:
......
...@@ -121,8 +121,8 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta ...@@ -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_ino = stat->ino;
tmp.st_mode = stat->mode; tmp.st_mode = stat->mode;
tmp.st_nlink = stat->nlink; tmp.st_nlink = stat->nlink;
SET_OLDSTAT_UID(tmp, stat->uid); SET_UID(tmp.st_uid, stat->uid);
SET_OLDSTAT_GID(tmp, stat->gid); SET_GID(tmp.st_gid, stat->gid);
tmp.st_rdev = old_encode_dev(stat->rdev); tmp.st_rdev = old_encode_dev(stat->rdev);
#if BITS_PER_LONG == 32 #if BITS_PER_LONG == 32
if (stat->size > MAX_NON_LFS) if (stat->size > MAX_NON_LFS)
...@@ -189,8 +189,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) ...@@ -189,8 +189,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
tmp.st_ino = stat->ino; tmp.st_ino = stat->ino;
tmp.st_mode = stat->mode; tmp.st_mode = stat->mode;
tmp.st_nlink = stat->nlink; tmp.st_nlink = stat->nlink;
SET_STAT_UID(tmp, stat->uid); SET_UID(tmp.st_uid, stat->uid);
SET_STAT_GID(tmp, stat->gid); SET_GID(tmp.st_gid, stat->gid);
#if BITS_PER_LONG == 32 #if BITS_PER_LONG == 32
tmp.st_rdev = old_encode_dev(stat->rdev); tmp.st_rdev = old_encode_dev(stat->rdev);
#else #else
......
...@@ -36,8 +36,8 @@ typedef struct { ...@@ -36,8 +36,8 @@ typedef struct {
int val[2]; int val[2];
} __kernel_fsid_t; } __kernel_fsid_t;
typedef __kernel_uid_t __kernel_old_uid_t; typedef unsigned short __kernel_old_uid_t;
typedef __kernel_gid_t __kernel_old_gid_t; typedef unsigned short __kernel_old_gid_t;
typedef __kernel_uid_t __kernel_uid32_t; typedef __kernel_uid_t __kernel_uid32_t;
typedef __kernel_gid_t __kernel_gid32_t; typedef __kernel_gid_t __kernel_gid32_t;
......
...@@ -35,6 +35,9 @@ ...@@ -35,6 +35,9 @@
extern int overflowuid; extern int overflowuid;
extern int overflowgid; extern int overflowgid;
extern void __bad_uid(void);
extern void __bad_gid(void);
#define DEFAULT_OVERFLOWUID 65534 #define DEFAULT_OVERFLOWUID 65534
#define DEFAULT_OVERFLOWGID 65534 #define DEFAULT_OVERFLOWGID 65534
...@@ -50,36 +53,36 @@ extern int overflowgid; ...@@ -50,36 +53,36 @@ extern int overflowgid;
#define low2highuid(uid) ((uid) == (old_uid_t)-1 ? (uid_t)-1 : (uid_t)(uid)) #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)) #define low2highgid(gid) ((gid) == (old_gid_t)-1 ? (gid_t)-1 : (gid_t)(gid))
/* Avoid extra ifdefs with these macros */ /* uid/gid input should be always 32bit uid_t */
#define SET_UID(var, uid) \
#define SET_UID16(var, uid) var = high2lowuid(uid) do { \
#define SET_GID16(var, gid) var = high2lowgid(gid) if (sizeof(var) == sizeof(old_uid_t)) (var) = high2lowuid(uid); \
#define NEW_TO_OLD_UID(uid) high2lowuid(uid) else if (sizeof(var) >= sizeof(uid)) (var) = (uid); \
#define NEW_TO_OLD_GID(gid) high2lowgid(gid) else __bad_uid(); \
#define OLD_TO_NEW_UID(uid) low2highuid(uid) } while(0)
#define OLD_TO_NEW_GID(gid) low2highgid(gid)
#define SET_GID(var, gid) \
/* specific to fs/stat.c */ do { \
#define SET_OLDSTAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) if (sizeof(var) == sizeof(old_gid_t)) (var) = high2lowgid(gid); \
#define SET_OLDSTAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) else if (sizeof(var) >= sizeof(gid)) (var) = (gid); \
#define SET_STAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) else __bad_gid(); \
#define SET_STAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) } while(0)
#else #else
#define SET_UID16(var, uid) do { ; } while (0) #define SET_UID(var,uid) \
#define SET_GID16(var, gid) do { ; } while (0) do { \
#define NEW_TO_OLD_UID(uid) (uid) if (sizeof(var) < sizeof(uid)) __bad_uid(); \
#define NEW_TO_OLD_GID(gid) (gid) (var) = (uid); \
#define OLD_TO_NEW_UID(uid) (uid) } while (0)
#define OLD_TO_NEW_GID(gid) (gid)
#define SET_OLDSTAT_UID(stat, uid) (stat).st_uid = (uid) #define SET_GID(var,gid) \
#define SET_OLDSTAT_GID(stat, gid) (stat).st_gid = (gid) do { \
#define SET_STAT_UID(stat, uid) (stat).st_uid = (uid) if (sizeof(var) < sizeof(gid)) __bad_gid(); \
#define SET_STAT_GID(stat, gid) (stat).st_gid = (gid) (var) = (gid); \
} while (0);
#endif /* CONFIG_UID16 */ #endif /* !CONFIG_UID16 */
/* /*
......
...@@ -421,10 +421,10 @@ void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out) ...@@ -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) void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
{ {
out->key = in->key; out->key = in->key;
out->uid = NEW_TO_OLD_UID(in->uid); SET_UID(out->uid, in->uid);
out->gid = NEW_TO_OLD_GID(in->gid); SET_GID(out->gid, in->gid);
out->cuid = NEW_TO_OLD_UID(in->cuid); SET_UID(out->cuid, in->cuid);
out->cgid = NEW_TO_OLD_GID(in->cgid); SET_GID(out->cgid, in->cgid);
out->mode = in->mode; out->mode = in->mode;
out->seq = in->seq; out->seq = in->seq;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment