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)
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;
......
......@@ -61,6 +61,7 @@
#include <linux/compat.h>
#include <linux/vfs.h>
#include <linux/ptrace.h>
#include <linux/highuid.h>
#include <asm/mman.h>
#include <asm/types.h>
#include <asm/uaccess.h>
......@@ -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;
}
......
......@@ -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;
......
......@@ -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 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
/* 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;
}
......
......@@ -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;
......
......@@ -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:
......
......@@ -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
......
......@@ -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;
......
......@@ -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 */
/*
......
......@@ -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;
}
......
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