Skip to content

Commit c54b245

Browse files
committed
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull user namespace rlimit handling update from Eric Biederman: "This is the work mainly by Alexey Gladkov to limit rlimits to the rlimits of the user that created a user namespace, and to allow users to have stricter limits on the resources created within a user namespace." * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: cred: add missing return error code when set_cred_ucounts() failed ucounts: Silence warning in dec_rlimit_ucounts ucounts: Set ucount_max to the largest positive value the type can hold kselftests: Add test to check for rlimit changes in different user namespaces Reimplement RLIMIT_MEMLOCK on top of ucounts Reimplement RLIMIT_SIGPENDING on top of ucounts Reimplement RLIMIT_MSGQUEUE on top of ucounts Reimplement RLIMIT_NPROC on top of ucounts Use atomic_t for ucounts reference counting Add a reference to ucounts for each cred Increase size of ucounts to atomic_long_t
2 parents e17c120 + 5e6b8a5 commit c54b245

File tree

29 files changed

+468
-127
lines changed

29 files changed

+468
-127
lines changed

fs/exec.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,10 @@ int begin_new_exec(struct linux_binprm * bprm)
13601360
WRITE_ONCE(me->self_exec_id, me->self_exec_id + 1);
13611361
flush_signal_handlers(me, 0);
13621362

1363+
retval = set_cred_ucounts(bprm->cred);
1364+
if (retval < 0)
1365+
goto out_unlock;
1366+
13631367
/*
13641368
* install the new credentials for this executable
13651369
*/
@@ -1874,7 +1878,7 @@ static int do_execveat_common(int fd, struct filename *filename,
18741878
* whether NPROC limit is still exceeded.
18751879
*/
18761880
if ((current->flags & PF_NPROC_EXCEEDED) &&
1877-
atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
1881+
is_ucounts_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) {
18781882
retval = -EAGAIN;
18791883
goto out_ret;
18801884
}

fs/hugetlbfs/inode.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,7 +1446,7 @@ static int get_hstate_idx(int page_size_log)
14461446
* otherwise hugetlb_reserve_pages reserves one less hugepages than intended.
14471447
*/
14481448
struct file *hugetlb_file_setup(const char *name, size_t size,
1449-
vm_flags_t acctflag, struct user_struct **user,
1449+
vm_flags_t acctflag, struct ucounts **ucounts,
14501450
int creat_flags, int page_size_log)
14511451
{
14521452
struct inode *inode;
@@ -1458,20 +1458,20 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
14581458
if (hstate_idx < 0)
14591459
return ERR_PTR(-ENODEV);
14601460

1461-
*user = NULL;
1461+
*ucounts = NULL;
14621462
mnt = hugetlbfs_vfsmount[hstate_idx];
14631463
if (!mnt)
14641464
return ERR_PTR(-ENOENT);
14651465

14661466
if (creat_flags == HUGETLB_SHMFS_INODE && !can_do_hugetlb_shm()) {
1467-
*user = current_user();
1468-
if (user_shm_lock(size, *user)) {
1467+
*ucounts = current_ucounts();
1468+
if (user_shm_lock(size, *ucounts)) {
14691469
task_lock(current);
14701470
pr_warn_once("%s (%d): Using mlock ulimits for SHM_HUGETLB is deprecated\n",
14711471
current->comm, current->pid);
14721472
task_unlock(current);
14731473
} else {
1474-
*user = NULL;
1474+
*ucounts = NULL;
14751475
return ERR_PTR(-EPERM);
14761476
}
14771477
}
@@ -1498,9 +1498,9 @@ struct file *hugetlb_file_setup(const char *name, size_t size,
14981498

14991499
iput(inode);
15001500
out:
1501-
if (*user) {
1502-
user_shm_unlock(size, *user);
1503-
*user = NULL;
1501+
if (*ucounts) {
1502+
user_shm_unlock(size, *ucounts);
1503+
*ucounts = NULL;
15041504
}
15051505
return file;
15061506
}

fs/proc/array.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p)
284284
collect_sigign_sigcatch(p, &ignored, &caught);
285285
num_threads = get_nr_threads(p);
286286
rcu_read_lock(); /* FIXME: is this correct? */
287-
qsize = atomic_read(&__task_cred(p)->user->sigpending);
287+
qsize = get_ucounts_value(task_ucounts(p), UCOUNT_RLIMIT_SIGPENDING);
288288
rcu_read_unlock();
289289
qlim = task_rlimit(p, RLIMIT_SIGPENDING);
290290
unlock_task_sighand(p, &flags);

include/linux/cred.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ struct cred {
143143
#endif
144144
struct user_struct *user; /* real user ID subscription */
145145
struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
146+
struct ucounts *ucounts;
146147
struct group_info *group_info; /* supplementary groups for euid/fsgid */
147148
/* RCU deletion */
148149
union {
@@ -169,6 +170,7 @@ extern int set_security_override_from_ctx(struct cred *, const char *);
169170
extern int set_create_files_as(struct cred *, struct inode *);
170171
extern int cred_fscmp(const struct cred *, const struct cred *);
171172
extern void __init cred_init(void);
173+
extern int set_cred_ucounts(struct cred *);
172174

173175
/*
174176
* check for validity of credentials
@@ -369,6 +371,7 @@ static inline void put_cred(const struct cred *_cred)
369371

370372
#define task_uid(task) (task_cred_xxx((task), uid))
371373
#define task_euid(task) (task_cred_xxx((task), euid))
374+
#define task_ucounts(task) (task_cred_xxx((task), ucounts))
372375

373376
#define current_cred_xxx(xxx) \
374377
({ \
@@ -385,6 +388,7 @@ static inline void put_cred(const struct cred *_cred)
385388
#define current_fsgid() (current_cred_xxx(fsgid))
386389
#define current_cap() (current_cred_xxx(cap_effective))
387390
#define current_user() (current_cred_xxx(user))
391+
#define current_ucounts() (current_cred_xxx(ucounts))
388392

389393
extern struct user_namespace init_user_ns;
390394
#ifdef CONFIG_USER_NS

include/linux/hugetlb.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ static inline struct hugetlbfs_inode_info *HUGETLBFS_I(struct inode *inode)
451451
extern const struct file_operations hugetlbfs_file_operations;
452452
extern const struct vm_operations_struct hugetlb_vm_ops;
453453
struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct,
454-
struct user_struct **user, int creat_flags,
454+
struct ucounts **ucounts, int creat_flags,
455455
int page_size_log);
456456

457457
static inline bool is_file_hugepages(struct file *file)
@@ -471,7 +471,7 @@ static inline struct hstate *hstate_inode(struct inode *i)
471471
#define is_file_hugepages(file) false
472472
static inline struct file *
473473
hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag,
474-
struct user_struct **user, int creat_flags,
474+
struct ucounts **ucounts, int creat_flags,
475475
int page_size_log)
476476
{
477477
return ERR_PTR(-ENOSYS);

include/linux/mm.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,8 +1709,8 @@ extern bool can_do_mlock(void);
17091709
#else
17101710
static inline bool can_do_mlock(void) { return false; }
17111711
#endif
1712-
extern int user_shm_lock(size_t, struct user_struct *);
1713-
extern void user_shm_unlock(size_t, struct user_struct *);
1712+
extern int user_shm_lock(size_t, struct ucounts *);
1713+
extern void user_shm_unlock(size_t, struct ucounts *);
17141714

17151715
/*
17161716
* Parameter block passed down to zap_pte_range in exceptional cases.

include/linux/sched/user.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,9 @@
1212
*/
1313
struct user_struct {
1414
refcount_t __count; /* reference count */
15-
atomic_t processes; /* How many processes does this user have? */
16-
atomic_t sigpending; /* How many pending signals does this user have? */
1715
#ifdef CONFIG_EPOLL
1816
atomic_long_t epoll_watches; /* The number of file descriptors currently watched */
1917
#endif
20-
#ifdef CONFIG_POSIX_MQUEUE
21-
/* protected by mq_lock */
22-
unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */
23-
#endif
24-
unsigned long locked_shm; /* How many pages of mlocked shm ? */
2518
unsigned long unix_inflight; /* How many files in flight in unix sockets */
2619
atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */
2720

include/linux/shmem_fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ extern struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt,
6565
extern int shmem_zero_setup(struct vm_area_struct *);
6666
extern unsigned long shmem_get_unmapped_area(struct file *, unsigned long addr,
6767
unsigned long len, unsigned long pgoff, unsigned long flags);
68-
extern int shmem_lock(struct file *file, int lock, struct user_struct *user);
68+
extern int shmem_lock(struct file *file, int lock, struct ucounts *ucounts);
6969
#ifdef CONFIG_SHMEM
7070
extern const struct address_space_operations shmem_aops;
7171
static inline bool shmem_mapping(struct address_space *mapping)

include/linux/signal_types.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ typedef struct kernel_siginfo {
1313
__SIGINFO;
1414
} kernel_siginfo_t;
1515

16+
struct ucounts;
17+
1618
/*
1719
* Real Time signals may be queued.
1820
*/
@@ -21,7 +23,7 @@ struct sigqueue {
2123
struct list_head list;
2224
int flags;
2325
kernel_siginfo_t info;
24-
struct user_struct *user;
26+
struct ucounts *ucounts;
2527
};
2628

2729
/* flags values. */

include/linux/user_namespace.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,15 @@ enum ucount_type {
5454
UCOUNT_FANOTIFY_GROUPS,
5555
UCOUNT_FANOTIFY_MARKS,
5656
#endif
57+
UCOUNT_RLIMIT_NPROC,
58+
UCOUNT_RLIMIT_MSGQUEUE,
59+
UCOUNT_RLIMIT_SIGPENDING,
60+
UCOUNT_RLIMIT_MEMLOCK,
5761
UCOUNT_COUNTS,
5862
};
5963

64+
#define MAX_PER_NAMESPACE_UCOUNTS UCOUNT_RLIMIT_NPROC
65+
6066
struct user_namespace {
6167
struct uid_gid_map uid_map;
6268
struct uid_gid_map gid_map;
@@ -92,23 +98,42 @@ struct user_namespace {
9298
struct ctl_table_header *sysctls;
9399
#endif
94100
struct ucounts *ucounts;
95-
int ucount_max[UCOUNT_COUNTS];
101+
long ucount_max[UCOUNT_COUNTS];
96102
} __randomize_layout;
97103

98104
struct ucounts {
99105
struct hlist_node node;
100106
struct user_namespace *ns;
101107
kuid_t uid;
102-
int count;
103-
atomic_t ucount[UCOUNT_COUNTS];
108+
atomic_t count;
109+
atomic_long_t ucount[UCOUNT_COUNTS];
104110
};
105111

106112
extern struct user_namespace init_user_ns;
113+
extern struct ucounts init_ucounts;
107114

108115
bool setup_userns_sysctls(struct user_namespace *ns);
109116
void retire_userns_sysctls(struct user_namespace *ns);
110117
struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid, enum ucount_type type);
111118
void dec_ucount(struct ucounts *ucounts, enum ucount_type type);
119+
struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid);
120+
struct ucounts * __must_check get_ucounts(struct ucounts *ucounts);
121+
void put_ucounts(struct ucounts *ucounts);
122+
123+
static inline long get_ucounts_value(struct ucounts *ucounts, enum ucount_type type)
124+
{
125+
return atomic_long_read(&ucounts->ucount[type]);
126+
}
127+
128+
long inc_rlimit_ucounts(struct ucounts *ucounts, enum ucount_type type, long v);
129+
bool dec_rlimit_ucounts(struct ucounts *ucounts, enum ucount_type type, long v);
130+
bool is_ucounts_overlimit(struct ucounts *ucounts, enum ucount_type type, unsigned long max);
131+
132+
static inline void set_rlimit_ucount_max(struct user_namespace *ns,
133+
enum ucount_type type, unsigned long max)
134+
{
135+
ns->ucount_max[type] = max <= LONG_MAX ? max : LONG_MAX;
136+
}
112137

113138
#ifdef CONFIG_USER_NS
114139

0 commit comments

Comments
 (0)